home *** CD-ROM | disk | FTP | other *** search
/ Aminet 30 / Aminet 30 (1999)(Schatztruhe)[!][Apr 1999].iso / Aminet / gfx / misc / gnuplot-3.7src.lha / gnuplot-3.7src / gnuplot-3.7.lha / gnuplot-3.7 / graphics.c < prev    next >
C/C++ Source or Header  |  1999-01-12  |  132KB  |  4,509 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: graphics.c,v 1.138 1998/06/18 14:55:08 ddenholm Exp $";
  3. #endif
  4.  
  5. /* GNUPLOT - graphics.c */
  6.  
  7. /*[
  8.  * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted,
  12.  * provided that the above copyright notice appear in all copies and
  13.  * that both that copyright notice and this permission notice appear
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the complete modified source code.  Modifications are to
  18.  * be distributed as patches to the released version.  Permission to
  19.  * distribute binaries produced by compiling modified sources is granted,
  20.  * provided you
  21.  *   1. distribute the corresponding source modifications from the
  22.  *    released version in the form of a patch file along with the binaries,
  23.  *   2. add special version identification to distinguish your version
  24.  *    in addition to the base release version number,
  25.  *   3. provide your name and address as the primary contact for the
  26.  *    support of your modified version, and
  27.  *   4. retain our contact information in regard to use of the base
  28.  *    software.
  29.  * Permission to distribute the released version of the source code along
  30.  * with corresponding source modifications in the form of a patch file is
  31.  * granted with same provisions 2 through 4 for binary distributions.
  32.  *
  33.  * This software is provided "as is" without express or implied warranty
  34.  * to the extent permitted by applicable law.
  35. ]*/
  36.  
  37.  
  38. #include "plot.h"
  39. #include "setshow.h"
  40.  
  41. /* key placement is calculated in boundary, so we need file-wide variables
  42.  * To simplify adjustments to the key, we set all these once [depends on
  43.  * key_reverse] and use them throughout.
  44.  */
  45.  
  46. /*{{{  local and global variables */
  47. static int key_sample_width;    /* width of line sample */
  48. static int key_sample_left;    /* offset from x for left of line sample */
  49. static int key_sample_right;    /* offset from x for right of line sample */
  50. static int key_point_offset;    /* offset from x for point sample */
  51. static int key_text_left;    /* offset from x for left-justified text */
  52. static int key_text_right;    /* offset from x for right-justified text */
  53. static int key_size_left;    /* size of left bit of key (text or sample, depends on key_reverse) */
  54. static int key_size_right;    /* size of right part of key (including padding) */
  55.  
  56. /* I think the following should also be static ?? */
  57.  
  58. static int key_xl, key_xr, key_yt, key_yb;    /* boundarys for key field */
  59. static int max_ptitl_len = 0;    /* max length of plot-titles (keys) */
  60. static int ktitl_lines = 0;    /* no lines in key_title (key header) */
  61. static int ptitl_cnt;        /* count keys with len > 0  */
  62. static int key_cols;        /* no cols of keys */
  63. static int key_rows, key_col_wth, yl_ref;
  64.  
  65.  
  66. /* penalty for doing tics by callback in gen_tics is need for
  67.  * global variables to communicate with the tic routines
  68.  * Dont need to be arrays for this
  69.  */
  70. static int tic_start, tic_direction, tic_text, rotate_tics, tic_hjust, tic_vjust, tic_mirror;
  71.  
  72. /* set by tic_callback - how large to draw polar radii */
  73. static double largest_polar_circle;
  74.  
  75. /* either xformat etc or invented time format
  76.  * index with FIRST_X_AXIS etc
  77.  * global because used in gen_tics, which graph3d also uses
  78.  */
  79. char ticfmt[8][MAX_ID_LEN+1]; /* HBB 990106: fix buffer overrun */
  80. int timelevel[8];
  81. double ticstep[8];
  82.  
  83. static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
  84.  
  85. static int key_entry_height;    /* bigger of t->v_size, pointsize*t->v_tick */
  86. static int p_width, p_height;    /* pointsize * { t->h_tic | t->v_tic } */
  87.  
  88.  
  89. /* there are several things on right of plot - key, y2tics and y2label
  90.  * when working out boundary, save posn of y2label for later...
  91.  * Same goes for x2label.
  92.  * key posn is also stored in key_xl, and tics go at xright
  93.  */
  94. static int ylabel_x, y2label_x, xlabel_y, x2label_y, title_y, time_y, time_x;
  95. static int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
  96. /*}}} */
  97.  
  98. /*{{{  static fns and local macros */
  99. static void plot_impulses __PROTO((struct curve_points * plot, int yaxis_x,
  100.                    int xaxis_y));
  101. static void plot_lines __PROTO((struct curve_points * plot));
  102. static void plot_points __PROTO((struct curve_points * plot));
  103. static void plot_dots __PROTO((struct curve_points * plot));
  104. static void plot_bars __PROTO((struct curve_points * plot));
  105. static void plot_boxes __PROTO((struct curve_points * plot, int xaxis_y));
  106. static void plot_vectors __PROTO((struct curve_points * plot));
  107. static void plot_f_bars __PROTO((struct curve_points * plot));
  108. static void plot_c_bars __PROTO((struct curve_points * plot));
  109.  
  110. static void edge_intersect __PROTO((struct coordinate GPHUGE * points, int i,
  111.                     double *ex, double *ey));
  112. static int two_edge_intersect __PROTO((struct coordinate GPHUGE * points,
  113.                     int i, double *lx, double *ly));
  114. static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
  115.  
  116. static void plot_steps __PROTO((struct curve_points * plot));    /* JG */
  117. static void plot_fsteps __PROTO((struct curve_points * plot));    /* HOE */
  118. static void plot_histeps __PROTO((struct curve_points * plot));    /* CAC */
  119. static void histeps_horizontal __PROTO((int *xl, int *yl, double x1, double x2,
  120.                     double y));             /* CAC */
  121. static void histeps_vertical __PROTO((int *xl, int *yl, double x, double y1,
  122.                     double y2));        /* CAC */
  123. static void edge_intersect_steps __PROTO((struct coordinate GPHUGE * points,
  124.                 int i, double *ex, double *ey)); /* JG */
  125. static void edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points,
  126.                 int i, double *ex, double *ey)); /* HOE */
  127. static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));                /* JG */
  128. static TBOOLEAN two_edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
  129.  
  130. static double LogScale __PROTO((double coord, int is_log, double log_base_log,
  131.                 char *what, char *axis));
  132. static double dbl_raise __PROTO((double x, int y));
  133. static void boundary __PROTO((int scaling, struct curve_points * plots,
  134.                 int count));
  135. static double make_tics __PROTO((int axis, int guide));
  136.  
  137. /* widest2d_callback keeps longest so far in here */
  138. static int widest_tic;
  139.  
  140. static void widest2d_callback __PROTO((int axis, double place, char *text,
  141.                     struct lp_style_type grid));
  142. static void ytick2d_callback __PROTO((int axis, double place, char *text,
  143.                     struct lp_style_type grid));
  144. static void xtick2d_callback __PROTO((int axis, double place, char *text,
  145.                     struct lp_style_type grid));
  146. static void map_position __PROTO((struct position * pos, unsigned int *x,
  147.                     unsigned int *y, char *what));
  148. static void mant_exp __PROTO((double log_base, double x, int scientific,
  149.                 double *m, int *p));
  150. static void gprintf __PROTO((char *dest, size_t count, char *format,
  151.                 double log_base, double x));
  152.  
  153. #if defined(sun386) || defined(AMIGA_SC_6_1)
  154. static double CheckLog __PROTO((TBOOLEAN is_log, double base_log, double x));
  155. #endif
  156.  
  157. /* for plotting error bars
  158.  * half the width of error bar tic mark
  159.  */
  160. #define ERRORBARTIC (t->h_tic/2)
  161.  
  162. /*
  163.  * The Amiga SAS/C 6.2 compiler moans about macro envocations causing
  164.  * multiple calls to functions. I converted these macros to inline
  165.  * functions coping with the problem without loosing speed.
  166.  * If your compiler supports __inline, you should add it to the
  167.  * #ifdef directive
  168.  * (MGR, 1993)
  169.  */
  170.  
  171. #ifdef AMIGA_SC_6_1
  172. GP_INLINE static TBOOLEAN i_inrange(int z, int min, int max)
  173. {
  174.     return ((min < max) ? ((z >= min) && (z <= max)) : ((z >= max) && (z <= min)));
  175. }
  176.  
  177. GP_INLINE static double f_max(double a, double b)
  178. {
  179.     return (GPMAX(a, b));
  180. }
  181.  
  182. GP_INLINE static double f_min(double a, double b)
  183. {
  184.     return (GPMIN(a, b));
  185. }
  186.  
  187. #else
  188. #define f_max(a,b) GPMAX((a),(b))
  189. #define f_min(a,b) GPMIN((a),(b))
  190. #define i_inrange(z,a,b) inrange((z),(a),(b))
  191. #endif
  192.  
  193. /* True if a and b have the same sign or zero (positive or negative) */
  194. #define samesign(a,b) ((a) * (b) >= 0)
  195. /*}}} */
  196.  
  197. /*{{{  more variables */
  198. /* Define the boundary of the plot
  199.  * These are computed at each call to do_plot, and are constant over
  200.  * the period of one do_plot. They actually only change when the term
  201.  * type changes and when the 'set size' factors change.
  202.  * - no longer true, for 'set key out' or 'set key under'. also depend
  203.  * on tic marks and multi-line labels.
  204.  * They are shared with graph3d.c since we want to use its draw_clip_line()
  205.  */
  206. int xleft, xright, ybot, ytop;
  207.  
  208.  
  209. /* we make a local copy of the 'key' variable so that if something
  210.  * goes wrong, we can switch it off temporarily
  211.  */
  212.  
  213. static int lkey;
  214.  
  215. /* First attempt at double axes...
  216.  * x_min etc are now accessed from a global array min_array[], max_array[]
  217.  * put the scale factors into a similar array
  218.  * for convenience in this first attack on double axes, just define x_min etc
  219.  * since code already uses x_min, etc  Eventually it will be done properly
  220.  */
  221.  
  222.  
  223. extern double min_array[], max_array[];
  224. extern int auto_array[];
  225.  
  226. extern int log_array[];
  227. extern double base_array[], log_base_array[];
  228.  
  229. static int x_axis = FIRST_X_AXIS, y_axis = FIRST_Y_AXIS;    /* current axes */
  230.  
  231. static double scale[AXIS_ARRAY_SIZE];    /* scale factors for mapping for each axis */
  232.  
  233. /* BODGES BEFORE I FIX IT UP */
  234. #define x_min min_array[x_axis]
  235. #define x_max max_array[x_axis]
  236. #define y_min min_array[y_axis]
  237. #define y_max max_array[y_axis]
  238.  
  239. /* And the functions to map from user to terminal coordinates */
  240. /* maps floating point x to screen */
  241. #define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5)
  242. /* same for y */
  243. #define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5)
  244.  
  245. /* (DFK) Watch for cancellation error near zero on axes labels */
  246. /* less than one hundredth of a tic mark */
  247. #define SIGNIF (0.01)
  248. #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
  249. #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
  250. /*}}} */
  251.  
  252. /*{{{  CheckLog() */
  253. /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog 
  254.  * macro, so I write it as a function on that machine.
  255.  *
  256.  * Amiga SAS/C 6.2 thinks it will do too much work calling functions in
  257.  * macro arguments twice, thus I inline theese functions. (MGR, 1993)
  258.  * If your compiler doesn't handle those macros correctly, you should
  259.  * also subscribe here. Even without inlining you gain speed with log plots
  260.  */
  261. #if defined(sun386) || defined(AMIGA_SC_6_1)
  262. GP_INLINE static double CheckLog(is_log, base_log, x)
  263. TBOOLEAN is_log;
  264. double base_log;
  265. double x;
  266. {
  267.     if (is_log)
  268.     return (pow(base_log, x));
  269.     else
  270.     return (x);
  271. }
  272. #else
  273. /* (DFK) Use 10^x if logscale is in effect, else x */
  274. #define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
  275. #endif /* sun386 || SAS/C */
  276. /*}}} */
  277.  
  278. /*{{{  LogScale() */
  279. static double LogScale(coord, is_log, log_base_log, what, axis)
  280. double coord;            /* the value */
  281. TBOOLEAN is_log;        /* is this axis in logscale? */
  282. double log_base_log;        /* if so, the log of its base */
  283. char *what;            /* what is the coord for? */
  284. char *axis;            /* which axis is this for ("x" or "y")? */
  285. {
  286.     if (is_log) {
  287.     if (coord <= 0.0) {
  288.         char errbuf[100];    /* place to write error message */
  289.         (void) sprintf(errbuf, "%s has %s coord of %g; must be above 0 for log scale!",
  290.                what, axis, coord);
  291.         graph_error(errbuf);
  292.     } else
  293.         return (log(coord) / log_base_log);
  294.     }
  295.     return (coord);
  296. }
  297. /*}}} */
  298.  
  299. /*{{{  graph_error() */
  300. /* handle errors during graph-plot in a consistent way */
  301. void graph_error(text)
  302. char *text;
  303. {
  304.     multiplot = FALSE;
  305.     term_end_plot();
  306.     int_error(text, NO_CARET);
  307. }
  308.  
  309. /*}}} */
  310.  
  311.  
  312. /*{{{  fixup_range() */
  313. /*
  314.  * === SYNOPSIS ===
  315.  *
  316.  * This function checks whether the data and/or plot range in a given axis
  317.  * is too small (which would cause divide-by-zero and/or infinite-loop
  318.  * problems later on).  If so,
  319.  * - if autoscaling is in effect for this axis, we widen the range
  320.  * - otherwise, we abort with a call to  int_error()  (which prints out
  321.  *   a suitable error message, then (hopefully) aborts this command and
  322.  *   returns to the command prompt or whatever).
  323.  *
  324.  *
  325.  * === HISTORY AND DESIGN NOTES ===
  326.  *
  327.  * 1998 Oct 4, Jonathan Thornburg <jthorn@galileo.thp.univie.ac.at>
  328.  *
  329.  * This function used to be a (long) macro  FIXUP_RANGE(AXIS, WHICH)
  330.  * which was (identically!) defined in  plot2d.c  and  plot3d.c .  As
  331.  * well as now being a function instead of a macro, the logic is also
  332.  * changed:  The "too small" range test no longer depends on 'set zero'
  333.  * and is now properly scaled relative to the data magnitude.
  334.  *
  335.  * The key question in designing this function is the policy for just how
  336.  * much to widen the data range by, as a function of the data magnitude.
  337.  * This is to some extent a matter of taste.  IMHO the key criterion is
  338.  * that (at least) all of the following should (a) not infinite-loop, and
  339.  * (b) give correct plots, regardless of the 'set zero' setting:
  340.  *      plot 6.02e23            # a huge number >> 1 / FP roundoff level
  341.  *      plot 3                  # a "reasonable-sized" number
  342.  *      plot 1.23e-12           # a small number still > FP roundoff level
  343.  *      plot 1.23e-12 * sin(x)  # a small function still > FP roundoff level
  344.  *      plot 1.23e-45           # a tiny number << FP roundoff level
  345.  *      plot 1.23e-45 * sin(x)  # a tiny function << FP roundoff level
  346.  *      plot 0          # or (more commonly) a data file of all zeros
  347.  * That is, IMHO gnuplot should *never* infinite-loop, and it should *never*
  348.  * producing an incorrect or misleading plot.  In contrast, the old code
  349.  * would infinite-loop on most of these examples with 'set zero 0.0' in
  350.  * effect, or would plot the small-amplitude sine waves as the zero function
  351.  * with 'zero' set larger than the sine waves' amplitude.
  352.  *
  353.  * The current code plots all the above examples correctly and without
  354.  * infinite looping.
  355.  *
  356.  *
  357.  * === USAGE ===
  358.  *
  359.  * Arguments:
  360.  * axis = (in) An integer specifying which axis (x1, x2, y1, y2, z, etc)
  361.  *             we should do our stuff for.  We use this argument as an
  362.  *             index into the global arrays  {min,max,auto}_array .  In
  363.  *             practice this argument will typically be one of the constants
  364.  *              {FIRST,SECOND}_{X,Y,Z}_AXIS  defined in plot.h.
  365.  * axis_name --> (in) This argument should point to the character string
  366.  *                    name corresponding to  axis , e.g. "x", "y2", etc.
  367.  *                    We use this (only) in formatting warning/error messages.
  368.  *
  369.  * Global Variables:
  370.  * auto_array[axis] = (in) (defined in command.c) Bit-flags which tell
  371.  *                         [in some manner I don't fully understand :=( ]
  372.  *                         whether and/or how autoscaling is in effect for
  373.  *                         this axis.
  374.  * {min,max}_array = (in out) (defined in command.c) The data ranges which
  375.  *                            this function manipulates.
  376.  * c_token = (in) (defined in plot.h) Used in formatting an error message.
  377.  *
  378.  * Bugs:
  379.  * - If  strlen(axis_name) > strlen("%s") , we may overflow an
  380.  *   error-message buffer, which would be A Bad Thing.  Caveat caller...
  381.  */
  382. void fixup_range(axis, axis_name)
  383. int axis;
  384. char *axis_name;
  385. {
  386. #define MAX_AXIS_NAME_LEN    2    /* max legal strlen(axis_name) */
  387.  
  388. /* These two macro definitions set the range-widening policy: */
  389. #define FIXUP_RANGE__WIDEN_ZERO_ABS    1.0    /* widen [0:0] by
  390.                            +/- this absolute amount */
  391. #define FIXUP_RANGE__WIDEN_NONZERO_REL    0.01    /* widen [nonzero:nonzero] by
  392.                            -/+ this relative amount */
  393.  
  394.     double dmin = min_array[axis];
  395.     double dmax = max_array[axis];
  396.     if (dmax - dmin == 0.0) {
  397.     /* empty range */
  398.     if (auto_array[axis]) {
  399.         /* range came from autoscaling ==> widen it */
  400.         double widen = (dmax == 0.0)
  401.         ? FIXUP_RANGE__WIDEN_ZERO_ABS
  402.         : FIXUP_RANGE__WIDEN_NONZERO_REL * dmax;
  403.         fprintf(stderr,
  404.             "Warning: empty %s range [%g:%g], ",
  405.             axis_name, dmin, dmax);
  406.         min_array[axis] -= widen;
  407.         max_array[axis] += widen;
  408.         fprintf(stderr,
  409.             "adjusting to [%g:%g]\n",
  410.             min_array[axis], max_array[axis]);
  411.     } else {
  412.         /* user has explicitly set the range */
  413.         /* (to something empty) ==> we're in trouble */
  414.         char msg_buffer[MAX_LINE_LEN + 1];
  415.         sprintf(msg_buffer, "Can't plot with an empty %s range!", axis_name);
  416.         int_error(msg_buffer, c_token);    /* never returns */
  417.     }
  418.     }
  419. }
  420.  
  421. /*}}} */
  422.  
  423.  
  424. /*{{{  widest2d_callback() */
  425. /* we determine widest tick label by getting gen_ticks to call this
  426.  * routine with every label
  427.  */
  428.  
  429. static void widest2d_callback(axis, place, text, grid)
  430. int axis;
  431. double place;
  432. char *text;
  433. struct lp_style_type grid;
  434. {
  435.     int len = label_width(text, NULL);
  436.     if (len > widest_tic)
  437.     widest_tic = len;
  438. }
  439. /*}}} */
  440.  
  441.  
  442. /*{{{  boundary() */
  443. /* borders of plotting area
  444.  * computed once on every call to do_plot
  445.  *
  446.  * The order in which things is done is getting pretty critical:
  447.  *  ytop depends on title, x2label, ylabels (if no rotated text)
  448.  *  ybot depends on key, if TUNDER
  449.  *  once we have these, we can setup the y1 and y2 tics and the 
  450.  *  only then can we calculate xleft and xright
  451.  *  xright depends also on key TRIGHT
  452.  *  then we can do x and x2 tics
  453.  *
  454.  * For set size ratio ..., everything depends on everything else...
  455.  * not really a lot we can do about that, so we lose if the plot has to
  456.  * be reduced vertically. But the chances are the
  457.  * change will not be very big, so the number of tics will not
  458.  * change dramatically.
  459.  *
  460.  * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
  461.  */
  462.  
  463. static void boundary(scaling, plots, count)
  464. TBOOLEAN scaling;        /* TRUE if terminal is doing the scaling */
  465. struct curve_points *plots;
  466. int count;
  467. {
  468.     int ytlen;
  469.     int yticlin = 0, y2ticlin = 0, timelin = 0;
  470.  
  471.     register struct termentry *t = term;
  472.     int key_h, key_w;
  473.     int can_rotate = (*t->text_angle) (1);
  474.  
  475.     int xtic_textheight;    /* height of xtic labels */
  476.     int x2tic_textheight;    /* height of x2tic labels */
  477.     int title_textheight;    /* height of title */
  478.     int xlabel_textheight;    /* height of xlabel */
  479.     int x2label_textheight;    /* height of x2label */
  480.     int timetop_textheight;    /* height of timestamp (if at top) */
  481.     int timebot_textheight;    /* height of timestamp (if at bottom) */
  482.     int ylabel_textheight;    /* height of (unrotated) ylabel */
  483.     int y2label_textheight;    /* height of (unrotated) y2label */
  484.     int ylabel_textwidth;    /* width of (rotated) ylabel */
  485.     int y2label_textwidth;    /* width of (rotated) y2label */
  486.     int timelabel_textwidth;    /* width of timestamp */
  487.     int ytic_textwidth;        /* width of ytic labels */
  488.     int y2tic_textwidth;    /* width of y2tic labels */
  489.     int x2tic_height;        /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
  490.     int xtic_height;
  491.     int ytic_width;
  492.     int y2tic_width;
  493.  
  494.     /* figure out which rotatable items are to be rotated
  495.      * (ylabel and y2label are rotated if possible) */
  496.     int vertical_timelabel = can_rotate && timelabel_rotate;
  497.     int vertical_xtics = can_rotate && rotate_xtics;
  498.     int vertical_x2tics = can_rotate && rotate_x2tics;
  499.     int vertical_ytics = can_rotate && rotate_ytics;
  500.     int vertical_y2tics = can_rotate && rotate_y2tics;
  501.  
  502.     lkey = key;            /* but we may have to disable it later */
  503.  
  504.     xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
  505.  
  506.     /*{{{  count lines in labels and tics */
  507.     if (*title.text)
  508.     label_width(title.text, &titlelin);
  509.     if (*xlabel.text)
  510.     label_width(xlabel.text, &xlablin);
  511.     if (*x2label.text)
  512.     label_width(x2label.text, &x2lablin);
  513.     if (*ylabel.text)
  514.     label_width(ylabel.text, &ylablin);
  515.     if (*y2label.text)
  516.     label_width(y2label.text, &y2lablin);
  517.     if (xtics)
  518.     label_width(xformat, &xticlin);
  519.     if (x2tics)
  520.     label_width(x2format, &x2ticlin);
  521.     if (ytics)
  522.     label_width(yformat, &yticlin);
  523.     if (y2tics)
  524.     label_width(y2format, &y2ticlin);
  525.     if (*timelabel.text)
  526.     label_width(timelabel.text, &timelin);
  527.     /*}}} */
  528.  
  529.     /*{{{  preliminary ytop  calculation */
  530.  
  531.     /*     first compute heights of things to be written in the margin */
  532.  
  533.     /* title */
  534.     if (titlelin)
  535.     title_textheight = (int) ((titlelin + title.yoffset + 1) * (t->v_char));
  536.     else
  537.     title_textheight = 0;
  538.  
  539.     /* x2label */
  540.     if (x2lablin) {
  541.     x2label_textheight = (int) ((x2lablin + x2label.yoffset) * (t->v_char));
  542.     if (!x2tics)
  543.         x2label_textheight += 0.5 * t->v_char;
  544.     } else
  545.     x2label_textheight = 0;
  546.  
  547.     /* tic labels */
  548.     if (x2tics & TICS_ON_BORDER) {
  549.     /* ought to consider tics on axes if axis near border */
  550.     if (vertical_x2tics) {
  551.         /* guess at tic length, since we don't know it yet
  552.            --- we'll fix it after the tic labels have been created */
  553.         x2tic_textheight = (int) (5 * (t->h_char));
  554.     } else
  555.         x2tic_textheight = (int) ((x2ticlin) * (t->v_char));
  556.     } else
  557.     x2tic_textheight = 0;
  558.  
  559.     /* tics */
  560.     if (!tic_in && ((x2tics & TICS_ON_BORDER) || ((xtics & TICS_MIRROR) && (xtics & TICS_ON_BORDER))))
  561.     x2tic_height = (int) ((t->v_tic) * ticscale);
  562.     else
  563.     x2tic_height = 0;
  564.  
  565.     /* timestamp */
  566.     if (*timelabel.text && !timelabel_bottom)
  567.     timetop_textheight = (int) ((timelin + timelabel.yoffset + 2) * (t->v_char));
  568.     else
  569.     timetop_textheight = 0;
  570.  
  571.     /* horizontal ylabel */
  572.     if (*ylabel.text && !can_rotate)
  573.     ylabel_textheight = (int) ((ylablin + ylabel.yoffset) * (t->v_char));
  574.     else
  575.     ylabel_textheight = 0;
  576.  
  577.     /* horizontal y2label */
  578.     if (*y2label.text && !can_rotate)
  579.     y2label_textheight = (int) ((y2lablin + y2label.yoffset) * (t->v_char));
  580.     else
  581.     y2label_textheight = 0;
  582.  
  583.     /* compute ytop from the various components
  584.      *     unless tmargin is explicitly specified  */
  585.  
  586.     ytop = (int) ((ysize + yoffset) * (t->ymax));
  587.  
  588.     if (tmargin < 0) {
  589.     int top_margin = x2label_textheight + title_textheight;
  590.     if (timetop_textheight + ylabel_textheight > top_margin)
  591.         top_margin = timetop_textheight + ylabel_textheight;
  592.     if (y2label_textheight > top_margin)
  593.         top_margin = y2label_textheight;
  594.  
  595.     top_margin += x2tic_height + x2tic_textheight;
  596. /* FIXME: what is this additional space reservation for??? */
  597.     if (top_margin > x2tic_height)
  598.         top_margin += (int) (t->v_char);
  599.  
  600.     ytop -= top_margin;
  601.     if (ytop == (int) ((ysize + yoffset) * (t->ymax))) {
  602.         /* make room for the end of rotated ytics or y2tics */
  603.         ytop -= (int) ((t->h_char) * 2);
  604.     }
  605.     } else
  606.     ytop -= (int) ((t->v_char) * tmargin);
  607.  
  608.     /*  end of preliminary ytop calculation }}} */
  609.  
  610.  
  611.     /*{{{  tentative xleft, needed for TUNDER */
  612.     if (lmargin >= 0)
  613.     xleft = (int) (xoffset * (t->xmax) + (t->h_char) * lmargin);
  614.     else
  615.     xleft = (int) (xoffset * (t->xmax) + (t->h_char) * 2);
  616.     /*}}} */
  617.  
  618.  
  619.     /*{{{  tentative xright, needed for TUNDER */
  620.     if (rmargin >= 0)
  621.     xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * rmargin);
  622.     else
  623.     xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * 2);
  624.     /*}}} */
  625.  
  626.  
  627.     /*{{{  preliminary ybot calculation
  628.      *     first compute heights of labels and tics */
  629.  
  630.     /* tic labels */
  631.     if (xtics & TICS_ON_BORDER) {
  632.     /* ought to consider tics on axes if axis near border */
  633.     if (vertical_xtics) {
  634.         /* guess at tic length, since we don't know it yet */
  635.         xtic_textheight = (int) ((t->h_char) * 5);
  636.     } else
  637.         xtic_textheight = (int) ((t->v_char) * (xticlin + 1));
  638.     } else
  639.     xtic_textheight = 0;
  640.  
  641.     /* tics */
  642.     if (!tic_in && ((xtics & TICS_ON_BORDER) || ((x2tics & TICS_MIRROR) && (x2tics & TICS_ON_BORDER))))
  643.     xtic_height = (int) ((t->v_tic) * ticscale);
  644.     else
  645.     xtic_height = 0;
  646.  
  647.     /* xlabel */
  648.     if (xlablin) {
  649.     /* offset is subtracted because if . 0, the margin is smaller */
  650.     xlabel_textheight = (int) ((xlablin - xlabel.yoffset) * (t->v_char));
  651.     if (!xtics)
  652.         xlabel_textheight += 0.5 * t->v_char;
  653.     } else
  654.     xlabel_textheight = 0;
  655.  
  656.     /* timestamp */
  657.     if (*timelabel.text && timelabel_bottom) {
  658.     /* && !vertical_timelabel)
  659.      * DBT 11-18-98 resize plot for vertical timelabels too !
  660.      */
  661.     /* offset is subtracted because if . 0, the margin is smaller */
  662.     timebot_textheight = (int) ((timelin - timelabel.yoffset + 1.5) * (t->v_char));
  663.     }
  664.     else
  665.     timebot_textheight = 0;
  666.  
  667.     /* compute ybot from the various components
  668.      *     unless bmargin is explicitly specified  */
  669.  
  670.     ybot = (int) ((t->ymax) * yoffset);
  671.  
  672.     if (bmargin < 0) {
  673.     ybot += xtic_height + xtic_textheight;
  674.     if (timebot_textheight > 0 || xlabel_textheight > 0)
  675.         ybot += (timebot_textheight > xlabel_textheight) ? timebot_textheight : xlabel_textheight;
  676.     if (ybot == (t->ymax) * yoffset) {
  677.         /* make room for the end of rotated ytics or y2tics */
  678.         ybot += (int) ((t->h_char) * 2);
  679.     }
  680.     } else
  681.     ybot += (int) (bmargin * (t->v_char));
  682.  
  683.     /*  end of preliminary ybot calculation }}} */
  684.  
  685.  
  686. #define KEY_PANIC(x) if (x) { lkey = 0; goto key_escape; }
  687.  
  688.     if (lkey) {
  689.     /*{{{  essential key features */
  690.     p_width = pointsize * t->h_tic;
  691.     p_height = pointsize * t->v_tic;
  692.  
  693.     if (key_swidth >= 0)
  694.         key_sample_width = key_swidth * (t->h_char) + p_width;
  695.     else
  696.         key_sample_width = 0;
  697.  
  698.     key_entry_height = p_height * 1.25 * key_vert_factor;
  699.     if (key_entry_height < (t->v_char))
  700.         key_entry_height = (t->v_char) * key_vert_factor;
  701.  
  702.     /* count max_len key and number keys with len > 0 */
  703.     max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
  704.     if ((ytlen = label_width(key_title, &ktitl_lines)) > max_ptitl_len)
  705.         max_ptitl_len = ytlen;
  706.  
  707.     if (key_reverse) {
  708.         key_sample_left = -key_sample_width;
  709.         key_sample_right = 0;
  710.         /* if key width is being used, adjust right-justified text */
  711.         key_text_left = t->h_char;
  712.         key_text_right = (t->h_char) * (max_ptitl_len + 1 + key_width_fix);
  713.         key_size_left = t->h_char - key_sample_left;    /* sample left is -ve */
  714.         key_size_right = key_text_right;
  715.     } else {
  716.         key_sample_left = 0;
  717.         key_sample_right = key_sample_width;
  718.         /* if key width is being used, adjust left-justified text */
  719.         key_text_left = -(int) ((t->h_char) * (max_ptitl_len + 1 + key_width_fix));
  720.         key_text_right = -(int) (t->h_char);
  721.         key_size_left = -key_text_left;
  722.         key_size_right = key_sample_right + t->h_char;
  723.     }
  724.     key_point_offset = (key_sample_left + key_sample_right) / 2;
  725.  
  726.     /* advance width for cols */
  727.     key_col_wth = key_size_left + key_size_right;
  728.  
  729.     key_rows = ptitl_cnt;
  730.     key_cols = 1;
  731.  
  732.     /* calculate rows and cols for key - if something goes wrong,
  733.      * the tidiest way out is to  set lkey = 0, and a goto
  734.      */
  735.  
  736.     if (lkey == -1) {
  737.         if (key_vpos == TUNDER) {
  738.         /* maximise no cols, limited by label-length */
  739.         key_cols = (int) (xright - xleft) / key_col_wth;
  740.         KEY_PANIC(key_cols == 0);
  741.         key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
  742.         KEY_PANIC(key_rows == 0);
  743.         /* now calculate actual no cols depending on no rows */
  744.         key_cols = (int) (ptitl_cnt + key_rows - 1) / key_rows;
  745.         KEY_PANIC(key_cols == 0);
  746.         key_col_wth = (int) (xright - xleft) / key_cols;
  747.         /* we divide into columns, then centre in column by considering
  748.          * ratio of * key_left_size to key_right_size
  749.          *
  750.          * key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
  751.          * do one integer division to maximise accuracy (hope we
  752.          * don't overflow !)
  753.          */
  754.         key_xl = xleft - key_size_left + ((xright - xleft) * key_size_left) / (key_cols * (key_size_left + key_size_right));
  755.         key_xr = key_xl + key_col_wth * (key_cols - 1) + key_size_left + key_size_right;
  756.         key_yb = t->ymax * yoffset;
  757.         key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
  758.         ybot += key_entry_height * key_rows + (int) ((t->v_char) * (ktitl_lines + 1));
  759.         } else {
  760.         /* maximise no rows, limited by ytop-ybot */
  761.         int i = (int) (ytop - ybot - (ktitl_lines + 1) * (t->v_char)) / key_entry_height;
  762.         KEY_PANIC(i == 0);
  763.         if (ptitl_cnt > i) {
  764.             key_cols = (int) (ptitl_cnt + i - 1) / i;
  765.             /* now calculate actual no rows depending on no cols */
  766.             KEY_PANIC(key_cols == 0);
  767.             key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
  768.         }
  769.         }
  770.         /* come here if we detect a division by zero in key calculations */
  771.       key_escape:
  772.         ;            /* ansi requires this */
  773.     }
  774.     /*}}} */
  775.     }
  776.     /*{{{  set up y and y2 tics */
  777.     {
  778.     /* setup_tics allows max number of tics to be specified
  779.      * but users dont like it to change with size and font,
  780.      * so we use value of 20, which is 3.5 behaviour.
  781.      * Note also that if format is '', yticlin = 0, so this gives
  782.      * division by zero. 
  783.      * int guide = (ytop-ybot)/term->v_char;
  784.      */
  785.     if (ytics)
  786.         setup_tics(FIRST_Y_AXIS, &yticdef, yformat, 20 /*(int) (guide/yticlin) */ );
  787.     if (y2tics)
  788.         setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, 20 /*(int) (guide/y2ticlin) */ );
  789.     }
  790.     /*}}} */
  791.  
  792.  
  793.     /*{{{  recompute xleft based on widths of ytics, ylabel etc
  794.        unless it has been explicitly set by lmargin */
  795.  
  796.     /* tic labels */
  797.     if (ytics & TICS_ON_BORDER) {
  798.     if (vertical_ytics)
  799.         /* HBB: we will later add some white space as part of this, so
  800.          * reserve two more rows (one above, one below the text ...).
  801.          * Same will be done to similar calc.'s elsewhere */
  802.         ytic_textwidth = (int) ((t->v_char) * (yticlin + 2));
  803.     else {
  804.         widest_tic = 0;    /* reset the global variable ... */
  805.         /* get gen_tics to call widest2d_callback with all labels
  806.          * the latter sets widest_tic to the length of the widest one
  807.          * ought to consider tics on axis if axis near border...
  808.          */
  809.         gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
  810.  
  811.         ytic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
  812.     }
  813.     } else {
  814.     ytic_textwidth = 0;
  815.     }
  816.  
  817.     /* tics */
  818.     if (!tic_in && ((ytics & TICS_ON_BORDER) || ((y2tics & TICS_MIRROR) && (y2tics & TICS_ON_BORDER))))
  819.     ytic_width = (int) ((t->h_tic) * ticscale);
  820.     else
  821.     ytic_width = 0;
  822.  
  823.     /* ylabel */
  824.     if (*ylabel.text && can_rotate) {
  825.     ylabel_textwidth = (int) ((ylablin - ylabel.xoffset) * (t->v_char));
  826.     if (!ytics)
  827.         ylabel_textwidth += 0.5 * t->v_char;
  828.     }
  829.     /* this should get large for NEGATIVE ylabel.xoffsets  DBT 11-5-98 */
  830.     else
  831.     ylabel_textwidth = 0;
  832.  
  833.     /* timestamp */
  834.     if (*timelabel.text && vertical_timelabel)
  835.     timelabel_textwidth = (int) ((timelin - timelabel.xoffset + 1.5) * (t->v_char));
  836.     else
  837.     timelabel_textwidth = 0;
  838.  
  839.     /* compute xleft from the various components
  840.      *     unless lmargin is explicitly specified  */
  841.  
  842.     xleft = (int) ((t->xmax) * xoffset);
  843.  
  844.     if (lmargin < 0) {
  845.     xleft += (timelabel_textwidth > ylabel_textwidth ? timelabel_textwidth : ylabel_textwidth)
  846.         + ytic_width + ytic_textwidth;
  847.  
  848.     if (xleft == (t->xmax) * xoffset) {
  849.         /* make room for end of xtic or x2tic label */
  850.         xleft += (int) ((t->h_char) * 2);
  851.     }
  852.     /* DBT 12-3-98  extra margin just in case */
  853.     xleft += 0.5 * t->v_char;
  854.     } else
  855.     xleft += (int) (lmargin * (t->h_char));
  856.  
  857.     /* make sure xleft is wide enough for a negatively
  858.      * x-offset horizontal timestamp
  859.      */
  860.     if (!vertical_timelabel && xleft - ytic_width - ytic_textwidth < -(int) (timelabel.xoffset * (t->h_char)))
  861.     xleft = ytic_width + ytic_textwidth - (int) (timelabel.xoffset * (t->h_char));
  862.     /*  end of xleft calculation }}} */
  863.  
  864.  
  865.     /*{{{  recompute xright based on widest y2tic. y2labels, key TOUT
  866.        unless it has been explicitly set by rmargin */
  867.  
  868.     /* tic labels */
  869.     if (y2tics & TICS_ON_BORDER) {
  870.     if (vertical_y2tics)
  871.         y2tic_textwidth = (int) ((t->v_char) * (y2ticlin + 2));
  872.     else {
  873.         widest_tic = 0;    /* reset the global variable ... */
  874.         /* get gen_tics to call widest2d_callback with all labels
  875.          * the latter sets widest_tic to the length of the widest one
  876.          * ought to consider tics on axis if axis near border...
  877.          */
  878.         gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
  879.  
  880.         y2tic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
  881.     }
  882.     } else {
  883.     y2tic_textwidth = 0;
  884.     }
  885.  
  886.     /* tics */
  887.     if (!tic_in && ((y2tics & TICS_ON_BORDER) || ((ytics & TICS_MIRROR) && (ytics & TICS_ON_BORDER))))
  888.     y2tic_width = (int) ((t->h_tic) * ticscale);
  889.     else
  890.     y2tic_width = 0;
  891.  
  892.     /* y2label */
  893.     if (can_rotate && *y2label.text) {
  894.     y2label_textwidth = (int) ((y2lablin + y2label.xoffset) * (t->v_char));
  895.     if (!y2tics)
  896.         y2label_textwidth += 0.5 * t->v_char;
  897.     } else
  898.     y2label_textwidth = 0;
  899.  
  900.     /* compute xright from the various components
  901.      *     unless rmargin is explicitly specified  */
  902.  
  903.     xright = (int) ((t->xmax) * (xsize + xoffset));
  904.  
  905.     if (rmargin < 0) {
  906.     /* xright -= y2label_textwidth + y2tic_width + y2tic_textwidth; */
  907.     xright -= y2tic_width + y2tic_textwidth;
  908.     if (y2label_textwidth > 0)
  909.         xright -= y2label_textwidth;
  910.  
  911.     /* adjust for outside key */
  912.     if (lkey == -1 && key_hpos == TOUT) {
  913.         xright -= key_col_wth * key_cols;
  914.         key_xl = xright + (int) (t->h_tic);
  915.     }
  916.     if (xright == (t->xmax) * (xsize + xoffset)) {
  917.         /* make room for end of xtic or x2tic label */
  918.         xright -= (int) ((t->h_char) * 2);
  919.     }
  920.     xright -= 0.5 * t->v_char;    /* DBT 12-3-98  extra margin just in case */
  921.  
  922.     } else
  923.     xright -= (int) (rmargin * (t->h_char));
  924.  
  925.     /*  end of xright calculation }}} */
  926.  
  927.  
  928.     if (aspect_ratio != 0.0) {
  929.     double current_aspect_ratio;
  930.  
  931.     if (aspect_ratio < 0 && (max_array[x_axis] - min_array[x_axis]) != 0.0) {
  932.         current_aspect_ratio = -aspect_ratio * (max_array[y_axis] - min_array[y_axis]) / (max_array[x_axis] - min_array[x_axis]);
  933.     } else
  934.         current_aspect_ratio = aspect_ratio;
  935.  
  936.     /*{{{  set aspect ratio if valid and sensible */
  937.     if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
  938.         double current = ((double) (ytop - ybot)) / ((double) (xright - xleft));
  939.         double required = (current_aspect_ratio * (double) t->v_tic) / ((double) t->h_tic);
  940.  
  941.         if (current > required) {
  942.         /* too tall */
  943.         ytop = ybot + required * (xright - xleft);
  944.         } else {
  945.         /* HBB: y2label_x wasn't defined yet, and would be
  946.          * overwritten later */
  947.         xright = xleft + (ytop - ybot) / required;
  948.         }
  949.     }
  950.     /*}}} */
  951.     }
  952.     /*{{{  set up x and x2 tics */
  953.     /* we should base the guide on the width of the xtics, but we cannot
  954.      * use widest_tics until tics are set up. Bit of a downer - let us
  955.      * assume tics are 5 characters wide
  956.      */
  957.  
  958.     {
  959.     /* see equivalent code for ytics above
  960.      * int guide = (xright - xleft) / (5*t->h_char);
  961.      */
  962.  
  963.     if (xtics)
  964.         setup_tics(FIRST_X_AXIS, &xticdef, xformat, 20 /*guide */ );
  965.     if (x2tics)
  966.         setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, 20 /*guide */ );
  967.     }
  968.     /*}}} */
  969.  
  970.  
  971.     /*  adjust top and bottom margins for tic label rotation */
  972.  
  973.     if (tmargin < 0 && x2tics & TICS_ON_BORDER && vertical_x2tics) {
  974.     widest_tic = 0;        /* reset the global variable ... */
  975.     gen_tics(SECOND_X_AXIS, &x2ticdef, 0, 0, 0.0, widest2d_callback);
  976. /* HBB: redid this: remove rough guess value first. Among other reasons,
  977.  * I suspected the '-4 lines' of the original code to be in error, as the
  978.  * original calc. of x2tic_textheight uses *5* lines */
  979.     ytop += x2tic_textheight;
  980. /* Now compute a new one and use that instead: */
  981.     x2tic_textheight = (int) ((t->h_char) * (widest_tic));
  982.     ytop -= x2tic_textheight;
  983.     }
  984.     if (bmargin < 0 && xtics & TICS_ON_BORDER && vertical_xtics) {
  985.     widest_tic = 0;        /* reset the global variable ... */
  986.     gen_tics(FIRST_X_AXIS, &xticdef, 0, 0, 0.0, widest2d_callback);
  987. /* HBB: same changes as for tmargin/ytop above */
  988.     ybot -= xtic_textheight;
  989.     xtic_textheight = (int) ((t->h_char) * widest_tic);
  990.     ybot += xtic_textheight;
  991.     }
  992.     /*  compute coordinates for axis labels, title et al
  993.      *     (some of these may not be used) */
  994.  
  995.     x2label_y = ytop + x2tic_height + x2tic_textheight + x2label_textheight;
  996.     if (x2tic_textheight && (title_textheight || x2label_textheight))
  997.     x2label_y += t->v_char;
  998.  
  999.     title_y = x2label_y + title_textheight;
  1000.  
  1001.     ylabel_y = ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
  1002.  
  1003.     y2label_y = ytop + x2tic_height + x2tic_textheight + y2label_textheight;
  1004.  
  1005.     xlabel_y = ybot - xtic_height - xtic_textheight - xlabel_textheight + t->v_char;
  1006.     ylabel_x = xleft - ytic_width - ytic_textwidth;
  1007.     if (*ylabel.text && can_rotate)
  1008.     ylabel_x -= ylabel_textwidth;
  1009.  
  1010.     y2label_x = xright + y2tic_width + y2tic_textwidth;
  1011.     if (*y2label.text && can_rotate)
  1012.     y2label_x += y2label_textwidth - t->v_char;
  1013.  
  1014.     if (vertical_timelabel) {
  1015.     if (timelabel_bottom)
  1016.         time_y = xlabel_y - timebot_textheight + xlabel_textheight;
  1017.     else {
  1018.         time_y = title_y + timetop_textheight - title_textheight - x2label_textheight;
  1019.     }
  1020.     } else {
  1021.     if (timelabel_bottom)
  1022.         time_y = xlabel_y - timebot_textheight + xlabel_textheight;
  1023.     else if (ylabel_textheight > 0)
  1024.         time_y = ylabel_y + timetop_textheight;
  1025.     else
  1026.         time_y = ytop + x2tic_height + x2tic_textheight + timetop_textheight + (int) (t->h_char);
  1027.     }
  1028.     if (vertical_timelabel)
  1029.     time_x = xleft - ytic_width - ytic_textwidth - timelabel_textwidth;
  1030.     else
  1031.     time_x = xleft - ytic_width - ytic_textwidth + (int) (timelabel.xoffset * (t->h_char));
  1032.  
  1033.     xtic_y = ybot - xtic_height - (vertical_xtics ? (int) (t->h_char) : (int) (t->v_char));
  1034.  
  1035.     x2tic_y = ytop + x2tic_height + (vertical_x2tics ? (int) (t->h_char) : x2tic_textheight);
  1036.  
  1037.     ytic_x = xleft - ytic_width - (vertical_ytics ? (ytic_textwidth - (int) t->v_char) : (int) (t->h_char));
  1038.  
  1039.     y2tic_x = xright + y2tic_width + (vertical_y2tics ? (int) (t->v_char) : (int) (t->h_char));
  1040.  
  1041.     /* restore text to horizontal [we tested rotation above] */
  1042.     (void) (*t->text_angle) (0);
  1043.  
  1044.     /* needed for map_position() below */
  1045.  
  1046.     scale[FIRST_Y_AXIS] = (ytop - ybot) / (max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
  1047.     scale[FIRST_X_AXIS] = (xright - xleft) / (max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
  1048.     scale[SECOND_Y_AXIS] = (ytop - ybot) / (max_array[SECOND_Y_AXIS] - min_array[SECOND_Y_AXIS]);
  1049.     scale[SECOND_X_AXIS] = (xright - xleft) / (max_array[SECOND_X_AXIS] - min_array[SECOND_X_AXIS]);
  1050.  
  1051.     /*{{{  calculate the window in the grid for the key */
  1052.     if (lkey == 1 || (lkey == -1 && key_vpos != TUNDER)) {
  1053.     /* calculate space for keys to prevent grid overwrite the keys */
  1054.     /* do it even if there is no grid, as do_plot will use these to position key */
  1055.     key_w = key_col_wth * key_cols;
  1056.     key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
  1057.     if (lkey == -1) {
  1058.         if (key_vpos == TTOP) {
  1059.         key_yt = (int) ytop - (t->v_tic);
  1060.         key_yb = key_yt - key_h;
  1061.         } else {
  1062.         key_yb = ybot + (t->v_tic);
  1063.         key_yt = key_yb + key_h;
  1064.         }
  1065.         if (key_hpos == TLEFT) {
  1066.         key_xl = xleft + (t->h_char);    /* for Left just */
  1067.         key_xr = key_xl + key_w;
  1068.         } else if (key_hpos == TRIGHT) {
  1069.         key_xr = xright - (t->h_char);    /* for Right just */
  1070.         key_xl = key_xr - key_w;
  1071.         } else {        /* TOUT */
  1072.         /* do this here for do_plot() */
  1073.         /* align right first since rmargin may be manual */
  1074.         key_xr = (xsize + xoffset) * (t->xmax) - (t->h_char);
  1075.         key_xl = key_xr - key_w;
  1076.         }
  1077.     } else {
  1078.         unsigned int x, y;
  1079.         map_position(&key_user_pos, &x, &y, "key");
  1080.         key_xl = x - key_size_left;
  1081.         key_xr = key_xl + key_w;
  1082.         key_yt = y + (ktitl_lines ? t->v_char : key_entry_height) / 2;
  1083.         key_yb = key_yt - key_h;
  1084.     }
  1085.     }
  1086.     /*}}} */
  1087.  
  1088. }
  1089.  
  1090. /*}}} */
  1091.  
  1092. /*{{{  dbl_raise() */
  1093. static double dbl_raise(x, y)
  1094. double x;
  1095. int y;
  1096. {
  1097.     register int i = abs(y);
  1098.     double val = 1.0;
  1099.  
  1100.     while (--i >= 0)
  1101.     val *= x;
  1102.  
  1103.     if (y < 0)
  1104.     return (1.0 / val);
  1105.     return (val);
  1106. }
  1107. /*}}} */
  1108.  
  1109. /*{{{  timetic_fmt() */
  1110. void timetic_format(axis, amin, amax)
  1111. int axis;
  1112. double amin, amax;
  1113. {
  1114.     struct tm tmin, tmax;
  1115.  
  1116.     *ticfmt[axis] = 0;        /* make sure we strcat to empty string */
  1117.  
  1118.     ggmtime(&tmin, (double) time_tic_just(timelevel[axis], amin));
  1119.     ggmtime(&tmax, (double) time_tic_just(timelevel[axis], amax));
  1120.     /*
  1121.        if ( tmax.tm_year == tmin.tm_year && 
  1122.        tmax.tm_mon == tmin.tm_mon && 
  1123.        tmax.tm_mday == tmin.tm_mday ) {
  1124.      */
  1125.     if (tmax.tm_year == tmin.tm_year &&
  1126.     tmax.tm_yday == tmin.tm_yday) {
  1127.     /* same day, skip date */
  1128.     if (tmax.tm_hour != tmin.tm_hour) {
  1129.         strcpy(ticfmt[axis], "%H");
  1130.     }
  1131.     if (timelevel[axis] < 3) {
  1132.         if (strlen(ticfmt[axis]))
  1133.         strcat(ticfmt[axis], ":");
  1134.         strcat(ticfmt[axis], "%M");
  1135.     }
  1136.     if (timelevel[axis] < 2) {
  1137.         strcat(ticfmt[axis], ":%S");
  1138.     }
  1139.     } else {
  1140.     if (tmax.tm_year != tmin.tm_year) {
  1141.         /* different years, include year in ticlabel */
  1142.         /* check convention, day/month or month/day */
  1143.         if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
  1144.         strcpy(ticfmt[axis], "%m/%d/%");
  1145.         } else {
  1146.         strcpy(ticfmt[axis], "%d/%m/%");
  1147.         }
  1148.         if (((int) (tmax.tm_year / 100)) != ((int) (tmin.tm_year / 100))) {
  1149.         strcat(ticfmt[axis], "Y");
  1150.         } else {
  1151.         strcat(ticfmt[axis], "y");
  1152.         }
  1153.  
  1154.     } else {
  1155.         if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
  1156.         strcpy(ticfmt[axis], "%m/%d");
  1157.         } else {
  1158.         strcpy(ticfmt[axis], "%d/%m");
  1159.         }
  1160.     }
  1161.     if (timelevel[axis] < 4) {
  1162.         strcat(ticfmt[axis], "\n%H:%M");
  1163.     }
  1164.     }
  1165. }
  1166. /*}}} */
  1167.  
  1168. /*{{{  set_tic() */
  1169. /* the guide parameter was intended to allow the number of tics
  1170.  * to depend on the relative sizes of the plot and the font.
  1171.  * It is the approximate upper limit on number of tics allowed.
  1172.  * But it did not go down well with the users.
  1173.  * A value of 20 gives the same behaviour as 3.5, so that is
  1174.  * hardwired into the calls to here. Maybe we will restore it
  1175.  * to the automatic calculation one day
  1176.  */
  1177.  
  1178. double set_tic(l10, guide)
  1179. double l10;
  1180. int guide;
  1181. {
  1182.     double xnorm, tics, posns;
  1183.  
  1184.     int fl = (int) floor(l10);
  1185.     xnorm = pow(10.0, l10 - fl);    /* approx number of decades */
  1186.  
  1187.     posns = guide / xnorm;    /* approx number of tic posns per decade */
  1188.  
  1189.     if (posns > 40)
  1190.     tics = 0.05;        /* eg 0, .05, .10, ... */
  1191.     else if (posns > 20)
  1192.     tics = 0.1;        /* eg 0, .1, .2, ... */
  1193.     else if (posns > 10)
  1194.     tics = 0.2;        /* eg 0,0.2,0.4,... */
  1195.     else if (posns > 4)
  1196.     tics = 0.5;        /* 0,0.5,1, */
  1197.     else if (posns > 1)
  1198.     tics = 1;        /* 0,1,2,.... */
  1199.     else if (posns > 0.5)
  1200.     tics = 2;        /* 0, 2, 4, 6 */
  1201.     else
  1202.     /* getting desperate... the ceil is to make sure we
  1203.      * go over rather than under - eg plot [-10:10] x*x
  1204.      * gives a range of about 99.999 - tics=xnorm gives
  1205.      * tics at 0, 99.99 and 109.98  - BAD !
  1206.      * This way, inaccuracy the other way will round
  1207.      * up (eg 0->100.0001 => tics at 0 and 101
  1208.      * I think latter is better than former
  1209.      */
  1210.     tics = ceil(xnorm);
  1211.  
  1212.     return (tics * dbl_raise(10.0, fl));
  1213. }
  1214. /*}}} */
  1215.  
  1216. /*{{{  make_tics() */
  1217. static double make_tics(axis, guide)
  1218. int axis, guide;
  1219. {
  1220.     register double xr, tic, l10;
  1221.  
  1222.     xr = fabs(min_array[axis] - max_array[axis]);
  1223.  
  1224.     l10 = log10(xr);
  1225.     tic = set_tic(l10, guide);
  1226.     if (log_array[axis] && tic < 1.0)
  1227.     tic = 1.0;
  1228.     if (datatype[axis] == TIME) {
  1229.     struct tm ftm, etm;
  1230.     /* this is not fun */
  1231.     ggmtime(&ftm, (double) min_array[axis]);
  1232.     ggmtime(&etm, (double) max_array[axis]);
  1233.  
  1234.     timelevel[axis] = 0;    /* seconds */
  1235.     if (tic > 20) {
  1236.         /* turn tic into units of minutes */
  1237.         tic = set_tic(log10(xr / 60.0), guide) * 60;
  1238.         timelevel[axis] = 1;    /* minutes */
  1239.     }
  1240.     if (tic > 20 * 60) {
  1241.         /* turn tic into units of hours */
  1242.         tic = set_tic(log10(xr / 3600.0), guide) * 3600;
  1243.         timelevel[axis] = 2;    /* hours */
  1244.     }
  1245.     if (tic > 2 * 3600) {
  1246.         /* need some tickling */
  1247.         tic = set_tic(log10(xr / (3 * 3600.0)), guide) * 3 * 3600;
  1248.     }
  1249.     if (tic > 6 * 3600) {
  1250.         /* turn tic into units of days */
  1251.         tic = set_tic(log10(xr / DAY_SEC), guide) * DAY_SEC;
  1252.         timelevel[axis] = 3;    /* days */
  1253.     }
  1254.     if (tic > 3 * DAY_SEC) {
  1255.         /* turn tic into units of weeks */
  1256.         tic = set_tic(log10(xr / WEEK_SEC), guide) * WEEK_SEC;
  1257.         if (tic < WEEK_SEC) {    /* force */
  1258.         tic = WEEK_SEC;
  1259.         }
  1260.         timelevel[axis] = 4;    /* weeks */
  1261.     }
  1262.     if (tic > 3 * WEEK_SEC) {
  1263.         /* turn tic into units of month */
  1264.         tic = set_tic(log10(xr / MON_SEC), guide) * MON_SEC;
  1265.         if (tic < MON_SEC) {    /* force */
  1266.         tic = MON_SEC;
  1267.         }
  1268.         timelevel[axis] = 5;    /* month */
  1269.     }
  1270.     if (tic > 2 * MON_SEC) {
  1271.         /* turn tic into units of month */
  1272.         tic = set_tic(log10(xr / (3 * MON_SEC)), guide) * 3 * MON_SEC;
  1273.     }
  1274.     if (tic > 6 * MON_SEC) {
  1275.         /* turn tic into units of years */
  1276.         tic = set_tic(log10(xr / YEAR_SEC), guide) * YEAR_SEC;
  1277.         if (tic < (YEAR_SEC / 2)) {
  1278.         tic = YEAR_SEC / 2;
  1279.         }
  1280.         timelevel[axis] = 6;    /* year */
  1281.     }
  1282.     }
  1283.     return (tic);
  1284. }
  1285. /*}}} */
  1286.  
  1287.  
  1288. void do_plot(plots, pcount)
  1289. struct curve_points *plots;
  1290. int pcount;            /* count of plots in linked list */
  1291. {
  1292.  
  1293. /* BODGES BEFORE I FIX IT UP */
  1294. #define ytic ticstep[y_axis]
  1295. #define xtic ticstep[x_axis]
  1296.  
  1297.     register struct termentry *t = term;
  1298.     register int curve;
  1299.     int axis_zero[AXIS_ARRAY_SIZE];    /* axes in terminal coords for FIRST_X_AXIS, etc */
  1300.     register struct curve_points *this_plot = NULL;
  1301.     register int xl = 0, yl = 0;    /* avoid gcc -Wall warning */
  1302.     register int key_count = 0;
  1303.     /* only a Pyramid would have this many registers! */
  1304.     struct text_label *this_label;
  1305.     struct arrow_def *this_arrow;
  1306.     TBOOLEAN scaling;
  1307.     char ss[MAX_LINE_LEN + 1], *s, *e;
  1308.  
  1309.     /* so that macros for x_min etc pick up correct values
  1310.      * until this is done properly
  1311.      */
  1312.  
  1313.     x_axis = FIRST_X_AXIS;
  1314.     y_axis = FIRST_Y_AXIS;
  1315.  
  1316. /*      Apply the desired viewport offsets. */
  1317.     if (y_min < y_max) {
  1318.     y_min -= boff;
  1319.     y_max += toff;
  1320.     } else {
  1321.     y_max -= boff;
  1322.     y_min += toff;
  1323.     }
  1324.     if (x_min < x_max) {
  1325.     x_min -= loff;
  1326.     x_max += roff;
  1327.     } else {
  1328.     x_max -= loff;
  1329.     x_min += roff;
  1330.     }
  1331.  
  1332.     /*
  1333.      * In the beginning, this "empty range" test was for exact
  1334.      * equality, eg  y_min == y_max , but that caused an infinite
  1335.      * loop once.  Then the test was changed to check for being
  1336.      * within the 'zero' threshold,  fabs(y_max - y_min) < zero) ,
  1337.      * but that prevented plotting data with ranges below 'zero'.
  1338.      * Now it's an absolute equality test again, since  fixup_range()
  1339.      * should have widened empty ranges before we get here.
  1340.      */
  1341.     if (x_min == x_max)
  1342.     int_error("x_min should not equal x_max!", NO_CARET);
  1343.     if (y_min == y_max)
  1344.     int_error("y_min should not equal y_max!", NO_CARET);
  1345.  
  1346.     term_init();        /* may set xmax/ymax */
  1347.  
  1348.     /* compute boundary for plot (xleft, xright, ytop, ybot)
  1349.      * also calculates tics, since xtics depend on xleft
  1350.      * but xleft depends on ytics. Boundary calculations
  1351.      * depend on term->v_char etc, so terminal must be
  1352.      * initialised.
  1353.      */
  1354.  
  1355.     scaling = (*t->scale) (xsize, ysize);
  1356.  
  1357.     boundary(scaling, plots, pcount);
  1358.  
  1359.     screen_ok = FALSE;
  1360.  
  1361.     term_start_plot();
  1362.  
  1363. /* DRAW TICS AND GRID */
  1364.     term_apply_lp_properties(&border_lp);    /* border linetype */
  1365.     largest_polar_circle = 0;
  1366.  
  1367.     /* select first mapping */
  1368.     x_axis = FIRST_X_AXIS;
  1369.     y_axis = FIRST_Y_AXIS;
  1370.  
  1371.     /* label first y axis tics */
  1372.     if (ytics) {
  1373.     int axis = map_x(ZERO);
  1374.     /* set the globals ytick2d_callback() needs */
  1375.  
  1376.     if (rotate_ytics && (*t->text_angle) (1)) {
  1377.         tic_hjust = CENTRE;
  1378.         tic_vjust = JUST_BOT;
  1379.         rotate_tics = 1;    /* HBB 980629 */
  1380.         ytic_x += t->v_char / 2;
  1381.     } else {
  1382.         tic_hjust = RIGHT;
  1383.         tic_vjust = JUST_CENTRE;
  1384.         rotate_tics = 0;    /* HBB 980629 */
  1385.     }
  1386.  
  1387.     if (ytics & TICS_MIRROR)
  1388.         tic_mirror = xright;
  1389.     else
  1390.         tic_mirror = -1;    /* no thank you */
  1391.  
  1392.     if ((ytics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
  1393.         tic_start = axis;
  1394.         tic_direction = -1;
  1395.         if (ytics & TICS_MIRROR)
  1396.         tic_mirror = tic_start;
  1397.         /* put text at boundary if axis is close to boundary */
  1398.         tic_text = (((tic_start - xleft) > (3 * t->h_char)) ? tic_start : xleft) - t->h_char;
  1399.     } else {
  1400.         tic_start = xleft;
  1401.         tic_direction = tic_in ? 1 : -1;
  1402.         tic_text = ytic_x;
  1403.     }
  1404.     /* go for it */
  1405.     gen_tics(FIRST_Y_AXIS, &yticdef,
  1406.          work_grid.l_type & (GRID_Y | GRID_MY),
  1407.          mytics, mytfreq, ytick2d_callback);
  1408.     (*t->text_angle) (0);    /* reset rotation angle */
  1409.  
  1410.     }
  1411.     /* label first x axis tics */
  1412.     if (xtics) {
  1413.     int axis = map_y(ZERO);
  1414.     /* set the globals xtick2d_callback() needs */
  1415.  
  1416.     if (rotate_xtics && (*t->text_angle) (1)) {
  1417.         tic_hjust = RIGHT;
  1418.         tic_vjust = JUST_CENTRE;
  1419.         rotate_tics = 1;    /* HBB 980629 */
  1420.     } else {
  1421.         tic_hjust = CENTRE;
  1422.         tic_vjust = JUST_TOP;
  1423.         rotate_tics = 0;    /* HBB 980629 */
  1424.     }
  1425.  
  1426.     if (xtics & TICS_MIRROR)
  1427.         tic_mirror = ytop;
  1428.     else
  1429.         tic_mirror = -1;    /* no thank you */
  1430.     if ((xtics & TICS_ON_AXIS) && !log_array[FIRST_Y_AXIS] && inrange(axis, ybot, ytop)) {
  1431.         tic_start = axis;
  1432.         tic_direction = -1;
  1433.         if (xtics & TICS_MIRROR)
  1434.         tic_mirror = tic_start;
  1435.         /* put text at boundary if axis is close to boundary */
  1436.         if (tic_start - ybot > 2 * t->v_char)
  1437.         tic_text = tic_start - ticscale * t->v_tic - t->v_char;
  1438.         else
  1439.         tic_text = ybot - t->v_char;
  1440.     } else {
  1441.         tic_start = ybot;
  1442.         tic_direction = tic_in ? 1 : -1;
  1443.         tic_text = xtic_y;
  1444.     }
  1445.     /* go for it */
  1446.     gen_tics(FIRST_X_AXIS, &xticdef,
  1447.          work_grid.l_type & (GRID_X | GRID_MX),
  1448.          mxtics, mxtfreq, xtick2d_callback);
  1449.     (*t->text_angle) (0);    /* reset rotation angle */
  1450.     }
  1451.     /* select second mapping */
  1452.     x_axis = SECOND_X_AXIS;
  1453.     y_axis = SECOND_Y_AXIS;
  1454.  
  1455.     /* label second y axis tics */
  1456.     if (y2tics) {
  1457.     /* set the globalss ytick2d_callback() needs */
  1458.     int axis = map_x(ZERO);
  1459.  
  1460.     if (rotate_y2tics && (*t->text_angle) (1)) {
  1461.         tic_hjust = CENTRE;
  1462.         tic_vjust = JUST_TOP;
  1463.         rotate_tics = 1;    /* HBB 980629 */
  1464.     } else {
  1465.         tic_hjust = LEFT;
  1466.         tic_vjust = JUST_CENTRE;
  1467.         rotate_tics = 0;    /* HBB 980629 */
  1468.     }
  1469.  
  1470.     if (y2tics & TICS_MIRROR)
  1471.         tic_mirror = xleft;
  1472.     else
  1473.         tic_mirror = -1;    /* no thank you */
  1474.     if ((y2tics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
  1475.         tic_start = axis;
  1476.         tic_direction = 1;
  1477.         if (y2tics & TICS_MIRROR)
  1478.         tic_mirror = tic_start;
  1479.         /* put text at boundary if axis is close to boundary */
  1480.         tic_text = (((xright - tic_start) > (3 * t->h_char)) ? tic_start : xright) + t->h_char;
  1481.     } else {
  1482.         tic_start = xright;
  1483.         tic_direction = tic_in ? -1 : 1;
  1484.         tic_text = y2tic_x;
  1485.     }
  1486.     /* go for it */
  1487.     gen_tics(SECOND_Y_AXIS, &y2ticdef,
  1488.          work_grid.l_type & (GRID_Y2 | GRID_MY2),
  1489.          my2tics, my2tfreq, ytick2d_callback);
  1490.     (*t->text_angle) (0);    /* reset rotation angle */
  1491.     }
  1492.     /* label second x axis tics */
  1493.     if (x2tics) {
  1494.     int axis = map_y(ZERO);
  1495.     /* set the globals xtick2d_callback() needs */
  1496.  
  1497.     if (rotate_x2tics && (*t->text_angle) (1)) {
  1498.         tic_hjust = LEFT;
  1499.         tic_vjust = JUST_CENTRE;
  1500.         rotate_tics = 1;    /* HBB 980629 */
  1501.     } else {
  1502.         tic_hjust = CENTRE;
  1503.         tic_vjust = JUST_BOT;
  1504.         rotate_tics = 0;    /* HBB 980629 */
  1505.     }
  1506.  
  1507.     if (x2tics & TICS_MIRROR)
  1508.         tic_mirror = ybot;
  1509.     else
  1510.         tic_mirror = -1;    /* no thank you */
  1511.     if ((x2tics & TICS_ON_AXIS) && !log_array[SECOND_Y_AXIS] && inrange(axis, ybot, ytop)) {
  1512.         tic_start = axis;
  1513.         tic_direction = 1;
  1514.         if (x2tics & TICS_MIRROR)
  1515.         tic_mirror = tic_start;
  1516.         /* put text at boundary if axis is close to boundary */
  1517.         tic_text = (((ytop - tic_start) > (2 * t->v_char)) ? tic_start : ytop) + t->v_char;
  1518.     } else {
  1519.         tic_start = ytop;
  1520.         tic_direction = tic_in ? -1 : 1;
  1521.         tic_text = x2tic_y;
  1522.     }
  1523.     /* go for it */
  1524.     gen_tics(SECOND_X_AXIS, &x2ticdef,
  1525.          work_grid.l_type & (GRID_X2 | GRID_MX2),
  1526.          mx2tics, mx2tfreq, xtick2d_callback);
  1527.     (*t->text_angle) (0);    /* reset rotation angle */
  1528.     }
  1529.     /* select first mapping */
  1530.     x_axis = FIRST_X_AXIS;
  1531.     y_axis = FIRST_Y_AXIS;
  1532.  
  1533. /* RADIAL LINES FOR POLAR GRID */
  1534.  
  1535.     /* note that draw_clip_line takes unsigneds, but (fortunately)
  1536.      * clip_line takes signeds
  1537.      */
  1538.     if (polar_grid_angle) {
  1539.     double theta = 0;
  1540.     int ox = map_x(0);
  1541.     int oy = map_y(0);
  1542.     term_apply_lp_properties(&grid_lp);
  1543.     for (theta = 0; theta < 6.29; theta += polar_grid_angle) {
  1544.         /* copy ox in case it gets moved (but it shouldn't) */
  1545.         int oox = ox;
  1546.         int ooy = oy;
  1547.         int x = map_x(largest_polar_circle * cos(theta));
  1548.         int y = map_y(largest_polar_circle * sin(theta));
  1549.         if (clip_line(&oox, &ooy, &x, &y)) {
  1550.         (*t->move) ((unsigned int) oox, (unsigned int) ooy);
  1551.         (*t->vector) ((unsigned int) x, (unsigned int) y);
  1552.         }
  1553.     }
  1554.     draw_clip_line(ox, oy,
  1555.                map_x(largest_polar_circle * cos(theta)),
  1556.                map_y(largest_polar_circle * sin(theta)));
  1557.     }
  1558. /* DRAW AXES */
  1559.  
  1560.     /* after grid so that axes linetypes are on top */
  1561.  
  1562.     x_axis = FIRST_X_AXIS;
  1563.     y_axis = FIRST_Y_AXIS;    /* chose scaling */
  1564.     axis_zero[FIRST_X_AXIS] = map_y(0.0);
  1565.     axis_zero[FIRST_Y_AXIS] = map_x(0.0);
  1566.  
  1567.     if (axis_zero[FIRST_X_AXIS] < ybot || is_log_y)
  1568.     axis_zero[FIRST_X_AXIS] = ybot;        /* save for impulse plotting */
  1569.     else if (axis_zero[FIRST_X_AXIS] >= ytop)
  1570.     axis_zero[FIRST_X_AXIS] = ytop;
  1571.     else if (xzeroaxis.l_type > -3) {
  1572.     term_apply_lp_properties(&xzeroaxis);
  1573.     (*t->move) (xleft, axis_zero[FIRST_X_AXIS]);
  1574.     (*t->vector) (xright, axis_zero[FIRST_X_AXIS]);
  1575.     }
  1576.     if ((yzeroaxis.l_type > -3) && !is_log_x
  1577.                 && axis_zero[FIRST_Y_AXIS] >= xleft
  1578.                 && axis_zero[FIRST_Y_AXIS] < xright) {
  1579.     term_apply_lp_properties(&yzeroaxis);
  1580.     (*t->move) (axis_zero[FIRST_Y_AXIS], ybot);
  1581.     (*t->vector) (axis_zero[FIRST_Y_AXIS], ytop);
  1582.     }
  1583.     x_axis = SECOND_X_AXIS;
  1584.     y_axis = SECOND_Y_AXIS;    /* chose scaling */
  1585.     axis_zero[SECOND_X_AXIS] = map_y(0.0);
  1586.     axis_zero[SECOND_Y_AXIS] = map_x(0.0);
  1587.  
  1588.     if (axis_zero[SECOND_X_AXIS] < ybot || is_log_y2)
  1589.     axis_zero[SECOND_X_AXIS] = ybot;    /* save for impulse plotting */
  1590.     else if (axis_zero[SECOND_X_AXIS] >= ytop)
  1591.     axis_zero[SECOND_X_AXIS] = ytop;
  1592.     else if (x2zeroaxis.l_type > -3) {
  1593.     term_apply_lp_properties(&x2zeroaxis);
  1594.     (*t->move) (xleft, axis_zero[SECOND_X_AXIS]);
  1595.     (*t->vector) (xright, axis_zero[SECOND_X_AXIS]);
  1596.     }
  1597.     if ((y2zeroaxis.l_type > -3) && !is_log_x2 && axis_zero[SECOND_Y_AXIS] >= xleft &&
  1598.     axis_zero[SECOND_Y_AXIS] < xright) {
  1599.     term_apply_lp_properties(&y2zeroaxis);
  1600.     (*t->move) (axis_zero[SECOND_Y_AXIS], ybot);
  1601.     (*t->vector) (axis_zero[SECOND_Y_AXIS], ytop);
  1602.     }
  1603.     /* DRAW PLOT BORDER */
  1604.     if (draw_border) {
  1605.     /* HBB 980609: just in case: move over to border linestyle only
  1606.      * if border is to be drawn */
  1607.     term_apply_lp_properties(&border_lp);    /* border linetype */
  1608.     (*t->move) (xleft, ybot);
  1609.     if (border_south) {
  1610.         (*t->vector) (xright, ybot);
  1611.     } else {
  1612.         (*t->move) (xright, ybot);
  1613.     }
  1614.     if (border_east) {
  1615.         (*t->vector) (xright, ytop);
  1616.     } else {
  1617.         (*t->move) (xright, ytop);
  1618.     }
  1619.     if (border_north) {
  1620.         (*t->vector) (xleft, ytop);
  1621.     } else {
  1622.         (*t->move) (xleft, ytop);
  1623.     }
  1624.     if (border_west) {
  1625.         (*t->vector) (xleft, ybot);
  1626.     } else {
  1627.         (*t->move) (xleft, ybot);
  1628.     }
  1629.     }
  1630. /* YLABEL */
  1631.     if (*ylabel.text) {
  1632.     strcpy(ss, ylabel.text);
  1633.     /* we worked out x-posn in boundary() */
  1634.     if ((*t->text_angle) (1)) {
  1635.         unsigned int x = ylabel_x + (t->v_char / 2);
  1636.         unsigned int y = (ytop + ybot) / 2 + ylabel.yoffset * (t->h_char);
  1637.         write_multiline(x, y, ss, CENTRE, JUST_TOP, 1, ylabel.font);
  1638.         (*t->text_angle) (0);
  1639.     } else {
  1640.         /* really bottom just, but we know number of lines 
  1641.            so we need to adjust x-posn by one line */
  1642.         unsigned int x = ylabel_x;
  1643.         unsigned int y = ylabel_y;
  1644.         write_multiline(x, y, ss, LEFT, JUST_TOP, 0, ylabel.font);
  1645.     }
  1646.     }
  1647. /* Y2LABEL */
  1648.     if (*y2label.text) {
  1649.     strcpy(ss, y2label.text);
  1650.     /* we worked out coordinates in boundary() */
  1651.     if ((*t->text_angle) (1)) {
  1652.         unsigned int x = y2label_x + (t->v_char / 2) - 1;
  1653.         unsigned int y = (ytop + ybot) / 2 + y2label.yoffset * (t->h_char);
  1654.         write_multiline(x, y, ss, CENTRE, JUST_TOP, 1, y2label.font);
  1655.         (*t->text_angle) (0);
  1656.     } else {
  1657.         /* really bottom just, but we know number of lines */
  1658.         unsigned int x = y2label_x;
  1659.         unsigned int y = y2label_y;
  1660.         write_multiline(x, y, ss, RIGHT, JUST_TOP, 0, y2label.font);
  1661.     }
  1662.     }
  1663. /* XLABEL */
  1664.     if (*xlabel.text) {
  1665.     unsigned int x = (xright + xleft) / 2 + xlabel.xoffset * (t->h_char);
  1666.     unsigned int y = xlabel_y - t->v_char / 2;    /* HBB */
  1667.     strcpy(ss, xlabel.text);
  1668.     write_multiline(x, y, ss, CENTRE, JUST_TOP, 0, xlabel.font);
  1669.     }
  1670. /* PLACE TITLE */
  1671.     if (*title.text) {
  1672.     /* we worked out y-coordinate in boundary() */
  1673.     unsigned int x = (xleft + xright) / 2 + title.xoffset * t->h_char;
  1674.     unsigned int y = title_y - t->v_char / 2;
  1675.     strcpy(ss, title.text);
  1676.     write_multiline(x, y, ss, CENTRE, JUST_TOP, 0, title.font);
  1677.     }
  1678. /* X2LABEL */
  1679.     if (*x2label.text) {
  1680.     /* we worked out y-coordinate in boundary() */
  1681.     unsigned int x = (xright + xleft) / 2 + x2label.xoffset * (t->h_char);
  1682.     unsigned int y = x2label_y - t->v_char / 2 - 1;
  1683.     strcpy(ss, x2label.text);
  1684.     write_multiline(x, y, ss, CENTRE, JUST_TOP, 0, x2label.font);
  1685.     }
  1686. /* PLACE TIMEDATE */
  1687.     if (*timelabel.text) {
  1688.     /* we worked out coordinates in boundary() */
  1689.     char str[MAX_LINE_LEN + 1];
  1690.     time_t now;
  1691.     unsigned int x = time_x;
  1692.     unsigned int y = time_y;
  1693.     time(&now);
  1694.     strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
  1695.  
  1696.     if (timelabel_rotate && (*t->text_angle) (1)) {
  1697.         x += t->v_char / 2;    /* HBB */
  1698.         if (timelabel_bottom)
  1699.         write_multiline(x, y, str, LEFT, JUST_TOP, 1, timelabel.font);
  1700.         else
  1701.         write_multiline(x, y, str, RIGHT, JUST_TOP, 1, timelabel.font);
  1702.         (*t->text_angle) (0);
  1703.     } else {
  1704.         y -= t->v_char / 2;    /* HBB */
  1705.         if (timelabel_bottom)
  1706.         write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
  1707.         else
  1708.         write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
  1709.     }
  1710.     }
  1711. /* PLACE LABELS */
  1712.     for (this_label = first_label; this_label != NULL;
  1713.      this_label = this_label->next) {
  1714.     unsigned int x, y;
  1715.     map_position(&this_label->place, &x, &y, "label");
  1716.     strcpy(ss, this_label->text);
  1717.     if (this_label->rotate && (*t->text_angle) (1)) {
  1718.         write_multiline(x, y, ss, this_label->pos, JUST_TOP, 1, this_label->font);
  1719.         (*t->text_angle) (0);
  1720.     } else {
  1721.         write_multiline(x, y, ss, this_label->pos, JUST_TOP, 0, this_label->font);
  1722.     }
  1723.     }
  1724.  
  1725. /* PLACE ARROWS */
  1726.     for (this_arrow = first_arrow; this_arrow != NULL;
  1727.      this_arrow = this_arrow->next) {
  1728.     unsigned int sx, sy, ex, ey;
  1729.     map_position(&this_arrow->start, &sx, &sy, "arrow");
  1730.     map_position(&this_arrow->end, &ex, &ey, "arrow");
  1731.  
  1732.     term_apply_lp_properties(&(this_arrow->lp_properties));
  1733.     (*t->arrow) (sx, sy, ex, ey, this_arrow->head);
  1734.     }
  1735.  
  1736. /* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
  1737.  
  1738.  
  1739.     if (lkey) {            /* may have been cancelled if something went wrong */
  1740.     /* just use key_xl etc worked out in boundary() */
  1741.     xl = key_xl + key_size_left;
  1742.     yl = key_yt;
  1743.  
  1744.     if (*key_title) {
  1745.         sprintf(ss, "%s\n", key_title);
  1746.         s = ss;
  1747.         yl -= t->v_char / 2;
  1748.         while ((e = (char *) strchr(s, '\n')) != NULL) {
  1749.         *e = '\0';
  1750.         if (key_just == JLEFT) {
  1751.             (*t->justify_text) (LEFT);
  1752.             (*t->put_text) (xl + key_text_left, yl, s);
  1753.         } else {
  1754.             if ((*t->justify_text) (RIGHT)) {
  1755.             (*t->put_text) (xl + key_text_right, yl, s);
  1756.             } else {
  1757.             int x = xl + key_text_right - (t->h_char) * strlen(s);
  1758.             if (key_hpos == TOUT || inrange(x, xleft, xright))
  1759.                 (*t->put_text) (x, yl, s);
  1760.             }
  1761.         }
  1762.         s = ++e;
  1763.         yl -= t->v_char;
  1764.         }
  1765.         yl += t->v_char / 2;
  1766.     }
  1767.     yl_ref = yl -= key_entry_height / 2;    /* centralise the keys */
  1768.     key_count = 0;
  1769.  
  1770.     if (key_box.l_type > -3) {
  1771.         term_apply_lp_properties(&key_box);
  1772.         (*t->move) (key_xl, key_yb);
  1773.         (*t->vector) (key_xl, key_yt);
  1774.         (*t->vector) (key_xr, key_yt);
  1775.         (*t->vector) (key_xr, key_yb);
  1776.         (*t->vector) (key_xl, key_yb);
  1777.         /* draw a horizontal line between key title
  1778.    and first entry                          *//* JFi */
  1779.         (*t->move) (key_xl, key_yt - (ktitl_lines) * t->v_char);    /* JFi */
  1780.         (*t->vector) (key_xr, key_yt - (ktitl_lines) * t->v_char);    /* JFi */
  1781.     }
  1782.     }                /* lkey */
  1783.     /* DRAW CURVES */
  1784.     this_plot = plots;
  1785.     for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
  1786.     int localkey = lkey;    /* a local copy */
  1787.  
  1788.     /* set scaling for this plot's axes */
  1789.     x_axis = this_plot->x_axis;
  1790.     y_axis = this_plot->y_axis;
  1791.  
  1792.     term_apply_lp_properties(&(this_plot->lp_properties));
  1793.  
  1794.     if (this_plot->title && !*this_plot->title) {
  1795.         localkey = 0;
  1796.     } else {
  1797.         if (localkey != 0 && this_plot->title) {
  1798.         key_count++;
  1799.         if (key_just == JLEFT) {
  1800.             (*t->justify_text) (LEFT);
  1801.             (*t->put_text) (xl + key_text_left,
  1802.                     yl, this_plot->title);
  1803.         } else {
  1804.             if ((*t->justify_text) (RIGHT)) {
  1805.             (*t->put_text) (xl + key_text_right,
  1806.                     yl, this_plot->title);
  1807.             } else {
  1808.             int x = xl + key_text_right - (t->h_char) * strlen(this_plot->title);
  1809.             if (key_hpos == TOUT ||
  1810.                 i_inrange(x, xleft, xright))
  1811.                 (*t->put_text) (x, yl, this_plot->title);
  1812.             }
  1813.         }
  1814.  
  1815.         /* draw sample depending on bits set in plot_style */
  1816.         if ((this_plot->plot_style & 1) ||
  1817.             ((this_plot->plot_style & 4) && this_plot->plot_type == DATA)) {    /* errors for data plots only */
  1818.             (*t->move) (xl + key_sample_left, yl);
  1819.             (*t->vector) (xl + key_sample_right, yl);
  1820.         }
  1821.         /* oops - doing the point sample now breaks postscript
  1822.          * terminal for example, which changes current line style
  1823.          * when drawing a point, but does not restore it.
  1824.          * We simply draw the point sample after plotting
  1825.          */
  1826.  
  1827.         if (this_plot->plot_type == DATA &&
  1828.             (this_plot->plot_style & 4) &&
  1829.             bar_size > 0.0) {
  1830.             (*t->move) (xl + key_sample_left, yl + ERRORBARTIC);
  1831.             (*t->vector) (xl + key_sample_left, yl - ERRORBARTIC);
  1832.             (*t->move) (xl + key_sample_right, yl + ERRORBARTIC);
  1833.             (*t->vector) (xl + key_sample_right, yl - ERRORBARTIC);
  1834.         }
  1835.         }
  1836.     }
  1837.  
  1838.     /* and now the curves, plus any special key requirements */
  1839.     /* be sure to draw all lines before drawing any points */
  1840.  
  1841.     switch (this_plot->plot_style) {
  1842.         /*{{{  IMPULSE */
  1843.     case IMPULSES:
  1844.         plot_impulses(this_plot, axis_zero[y_axis], axis_zero[x_axis]);
  1845.         break;
  1846.         /*}}} */
  1847.         /*{{{  LINES */
  1848.     case LINES:
  1849.         plot_lines(this_plot);
  1850.         break;
  1851.         /*}}} */
  1852.         /*{{{  STEPS */
  1853.     case STEPS:
  1854.         plot_steps(this_plot);
  1855.         break;
  1856.         /*}}} */
  1857.         /*{{{  FSTEPS */
  1858.     case FSTEPS:
  1859.         plot_fsteps(this_plot);
  1860.         break;
  1861.         /*}}} */
  1862.         /*{{{  HISTEPS */
  1863.     case HISTEPS:
  1864.         plot_histeps(this_plot);
  1865.         break;
  1866.         /*}}} */
  1867.         /*{{{  POINTSTYLE */
  1868.     case POINTSTYLE:
  1869.         plot_points(this_plot);
  1870.         break;
  1871.         /*}}} */
  1872.         /*{{{  LINESPOINTS */
  1873.     case LINESPOINTS:
  1874.         plot_lines(this_plot);
  1875.         plot_points(this_plot);
  1876.         break;
  1877.         /*}}} */
  1878.         /*{{{  DOTS */
  1879.     case DOTS:
  1880.         if (localkey != 0 && this_plot->title) {
  1881.         (*t->point) (xl + key_point_offset, yl, -1);
  1882.         }
  1883.         plot_dots(this_plot);
  1884.         break;
  1885.         /*}}} */
  1886.         /*{{{  YERRORBARS */
  1887.     case YERRORBARS:{
  1888.         plot_bars(this_plot);
  1889.         plot_points(this_plot);
  1890.         break;
  1891.         }
  1892.         /*}}} */
  1893.         /*{{{  XERRORBARS */
  1894.     case XERRORBARS:{
  1895.         plot_bars(this_plot);
  1896.         plot_points(this_plot);
  1897.         }
  1898.         /*}}} */
  1899.         /*{{{  XYERRORBARS */
  1900.     case XYERRORBARS:
  1901.         plot_bars(this_plot);
  1902.         plot_points(this_plot);
  1903.         break;
  1904.  
  1905.         /*}}} */
  1906.         /*{{{  BOXXYERROR */
  1907.     case BOXXYERROR:
  1908.         plot_boxes(this_plot, axis_zero[x_axis]);
  1909.         break;
  1910.         /*}}} */
  1911.         /*{{{  BOXERROR (falls through to) */
  1912.     case BOXERROR:
  1913.         plot_bars(this_plot);
  1914.         /* no break */
  1915.  
  1916.         /*}}} */
  1917.         /*{{{  BOXES */
  1918.     case BOXES:
  1919.         plot_boxes(this_plot, axis_zero[x_axis]);
  1920.         break;
  1921.         /*}}} */
  1922.         /*{{{  VECTOR */
  1923.     case VECTOR:
  1924.         plot_vectors(this_plot);
  1925.         break;
  1926.  
  1927.         /*}}} */
  1928.         /*{{{  FINANCEBARS */
  1929.     case FINANCEBARS:
  1930.         plot_f_bars(this_plot);
  1931.         break;
  1932.         /*}}} */
  1933.         /*{{{  CANDLESTICKS */
  1934.     case CANDLESTICKS:
  1935.         plot_c_bars(this_plot);
  1936.         break;
  1937.         /*}}} */
  1938.     }
  1939.  
  1940.  
  1941.     if (localkey && this_plot->title) {
  1942.         /* we deferred point sample until now */
  1943.         if (this_plot->plot_style & 2)
  1944.         (*t->point) (xl + key_point_offset, yl,
  1945.                  this_plot->lp_properties.p_type);
  1946.  
  1947.         if (key_count >= key_rows) {
  1948.         yl = yl_ref;
  1949.         xl += key_col_wth;
  1950.         key_count = 0;
  1951.         } else
  1952.         yl = yl - key_entry_height;
  1953.     }
  1954.     }
  1955.  
  1956.     term_end_plot();
  1957. }
  1958.  
  1959.  
  1960. /* BODGES */
  1961. #undef ytic
  1962. #undef xtic
  1963.  
  1964. /* plot_impulses:
  1965.  * Plot the curves in IMPULSES style
  1966.  */
  1967.  
  1968. static void plot_impulses(plot, yaxis_x, xaxis_y)
  1969. struct curve_points *plot;
  1970. int yaxis_x, xaxis_y;
  1971. {
  1972.     int i;
  1973.     int x, y;
  1974.     struct termentry *t = term;
  1975.  
  1976.     for (i = 0; i < plot->p_count; i++) {
  1977.     switch (plot->points[i].type) {
  1978.     case INRANGE:{
  1979.         x = map_x(plot->points[i].x);
  1980.         y = map_y(plot->points[i].y);
  1981.         break;
  1982.         }
  1983.     case OUTRANGE:{
  1984.         if (!inrange(plot->points[i].x, x_min, x_max))
  1985.             continue;
  1986.         x = map_x(plot->points[i].x);
  1987.         if ((y_min < y_max
  1988.              && plot->points[i].y < y_min)
  1989.             || (y_max < y_min
  1990.             && plot->points[i].y > y_min))
  1991.             y = map_y(y_min);
  1992.         else
  1993.             y = map_y(y_max);
  1994.  
  1995.         break;
  1996.         }
  1997.     default:        /* just a safety */
  1998.     case UNDEFINED:{
  1999.         continue;
  2000.         }
  2001.     }
  2002.  
  2003.     if (polar)
  2004.         (*t->move) (yaxis_x, xaxis_y);
  2005.     else
  2006.         (*t->move) (x, xaxis_y);
  2007.     (*t->vector) (x, y);
  2008.     }
  2009.  
  2010. }
  2011.  
  2012. /* plot_lines:
  2013.  * Plot the curves in LINES style
  2014.  */
  2015. static void plot_lines(plot)
  2016. struct curve_points *plot;
  2017. {
  2018.     int i;            /* point index */
  2019.     int x, y;            /* point in terminal coordinates */
  2020.     struct termentry *t = term;
  2021.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2022.     double ex, ey;        /* an edge point */
  2023.     double lx[2], ly[2];    /* two edge points */
  2024.  
  2025.     for (i = 0; i < plot->p_count; i++) {
  2026.     switch (plot->points[i].type) {
  2027.     case INRANGE:{
  2028.         x = map_x(plot->points[i].x);
  2029.         y = map_y(plot->points[i].y);
  2030.  
  2031.         if (prev == INRANGE) {
  2032.             (*t->vector) (x, y);
  2033.         } else if (prev == OUTRANGE) {
  2034.             /* from outrange to inrange */
  2035.             if (!clip_lines1) {
  2036.             (*t->move) (x, y);
  2037.             } else {
  2038.             edge_intersect(plot->points, i, &ex, &ey);
  2039.             (*t->move) (map_x(ex), map_y(ey));
  2040.             (*t->vector) (x, y);
  2041.             }
  2042.         } else {    /* prev == UNDEFINED */
  2043.             (*t->move) (x, y);
  2044.             (*t->vector) (x, y);
  2045.         }
  2046.  
  2047.         break;
  2048.         }
  2049.     case OUTRANGE:{
  2050.         if (prev == INRANGE) {
  2051.             /* from inrange to outrange */
  2052.             if (clip_lines1) {
  2053.             edge_intersect(plot->points, i, &ex, &ey);
  2054.             (*t->vector) (map_x(ex), map_y(ey));
  2055.             }
  2056.         } else if (prev == OUTRANGE) {
  2057.             /* from outrange to outrange */
  2058.             if (clip_lines2) {
  2059.             if (two_edge_intersect(plot->points, i, lx, ly)) {
  2060.                 (*t->move) (map_x(lx[0]), map_y(ly[0]));
  2061.                 (*t->vector) (map_x(lx[1]), map_y(ly[1]));
  2062.             }
  2063.             }
  2064.         }
  2065.         break;
  2066.         }
  2067.     default:        /* just a safety */
  2068.     case UNDEFINED:{
  2069.         break;
  2070.         }
  2071.     }
  2072.     prev = plot->points[i].type;
  2073.     }
  2074. }
  2075.  
  2076. /* XXX - JG  */
  2077. /* plot_steps:                          
  2078.  * Plot the curves in STEPS style
  2079.  */
  2080. static void plot_steps(plot)
  2081. struct curve_points *plot;
  2082. {
  2083.     int i;            /* point index */
  2084.     int x, y;            /* point in terminal coordinates */
  2085.     struct termentry *t = term;
  2086.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2087.     double ex, ey;        /* an edge point */
  2088.     double lx[2], ly[2];    /* two edge points */
  2089.     int yprev = 0;        /* previous point coordinates */
  2090.  
  2091.     for (i = 0; i < plot->p_count; i++) {
  2092.     switch (plot->points[i].type) {
  2093.     case INRANGE:{
  2094.         x = map_x(plot->points[i].x);
  2095.         y = map_y(plot->points[i].y);
  2096.  
  2097.         if (prev == INRANGE) {
  2098.             (*t->vector) (x, yprev);
  2099.             (*t->vector) (x, y);
  2100.         } else if (prev == OUTRANGE) {
  2101.             /* from outrange to inrange */
  2102.             if (!clip_lines1) {
  2103.             (*t->move) (x, y);
  2104.             } else {    /* find edge intersection */
  2105.             edge_intersect_steps(plot->points, i, &ex, &ey);
  2106.             (*t->move) (map_x(ex), map_y(ey));
  2107.             (*t->vector) (x, map_y(ey));
  2108.             (*t->vector) (x, y);
  2109.             }
  2110.         } else {    /* prev == UNDEFINED */
  2111.             (*t->move) (x, y);
  2112.             (*t->vector) (x, y);
  2113.         }
  2114.         yprev = y;
  2115.         break;
  2116.         }
  2117.     case OUTRANGE:{
  2118.         if (prev == INRANGE) {
  2119.             /* from inrange to outrange */
  2120.             if (clip_lines1) {
  2121.             edge_intersect_steps(plot->points, i, &ex, &ey);
  2122.             (*t->vector) (map_x(ex), yprev);
  2123.             (*t->vector) (map_x(ex), map_y(ey));
  2124.             }
  2125.         } else if (prev == OUTRANGE) {
  2126.             /* from outrange to outrange */
  2127.             if (clip_lines2) {
  2128.             if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
  2129.                 (*t->move) (map_x(lx[0]), map_y(ly[0]));
  2130.                 (*t->vector) (map_x(lx[1]), map_y(ly[0]));
  2131.                 (*t->vector) (map_x(lx[1]), map_y(ly[1]));
  2132.             }
  2133.             }
  2134.         }
  2135.         break;
  2136.         }
  2137.     default:        /* just a safety */
  2138.     case UNDEFINED:{
  2139.         break;
  2140.         }
  2141.     }
  2142.     prev = plot->points[i].type;
  2143.     }
  2144. }
  2145. /* XXX - HOE  */
  2146. /* plot_fsteps:                         
  2147.  * Plot the curves in STEPS style by step on forward yvalue
  2148.  */
  2149. static void plot_fsteps(plot)
  2150. struct curve_points *plot;
  2151. {
  2152.     int i;            /* point index */
  2153.     int x, y;            /* point in terminal coordinates */
  2154.     struct termentry *t = term;
  2155.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2156.     double ex, ey;        /* an edge point */
  2157.     double lx[2], ly[2];    /* two edge points */
  2158.     int xprev = 0;        /* previous point coordinates */
  2159.  
  2160.     for (i = 0; i < plot->p_count; i++) {
  2161.     switch (plot->points[i].type) {
  2162.     case INRANGE:{
  2163.         x = map_x(plot->points[i].x);
  2164.         y = map_y(plot->points[i].y);
  2165.  
  2166.         if (prev == INRANGE) {
  2167.             (*t->vector) (xprev, y);
  2168.             (*t->vector) (x, y);
  2169.         } else if (prev == OUTRANGE) {
  2170.             /* from outrange to inrange */
  2171.             if (!clip_lines1) {
  2172.             (*t->move) (x, y);
  2173.             } else {    /* find edge intersection */
  2174.             edge_intersect_fsteps(plot->points, i, &ex, &ey);
  2175.             (*t->move) (map_x(ex), map_y(ey));
  2176.             (*t->vector) (map_x(ex), y);
  2177.             (*t->vector) (x, y);
  2178.             }
  2179.         } else {    /* prev == UNDEFINED */
  2180.             (*t->move) (x, y);
  2181.             (*t->vector) (x, y);
  2182.         }
  2183.         xprev = x;
  2184.         break;
  2185.         }
  2186.     case OUTRANGE:{
  2187.         if (prev == INRANGE) {
  2188.             /* from inrange to outrange */
  2189.             if (clip_lines1) {
  2190.             edge_intersect_fsteps(plot->points, i, &ex, &ey);
  2191.             (*t->vector) (xprev, map_y(ey));
  2192.             (*t->vector) (map_x(ex), map_y(ey));
  2193.             }
  2194.         } else if (prev == OUTRANGE) {
  2195.             /* from outrange to outrange */
  2196.             if (clip_lines2) {
  2197.             if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
  2198.                 (*t->move) (map_x(lx[0]), map_y(ly[0]));
  2199.                 (*t->vector) (map_x(lx[0]), map_y(ly[1]));
  2200.                 (*t->vector) (map_x(lx[1]), map_y(ly[1]));
  2201.             }
  2202.             }
  2203.         }
  2204.         break;
  2205.         }
  2206.     default:        /* just a safety */
  2207.     case UNDEFINED:{
  2208.         break;
  2209.         }
  2210.     }
  2211.     prev = plot->points[i].type;
  2212.     }
  2213. }
  2214.  
  2215. /* CAC  */
  2216. /* plot_histeps:                                
  2217.  * Plot the curves in HISTEPS style
  2218.  */
  2219. static void plot_histeps(plot)
  2220. struct curve_points *plot;
  2221. {
  2222.     int i;            /* point index */
  2223.     int hold, bigi;        /* indices for sorting */
  2224.     int xl, yl;            /* cursor position in terminal coordinates */
  2225.     struct termentry *t = term;
  2226.     double x, y, xn, yn;    /* point position */
  2227.     int *gl, goodcount;        /* array to hold list of valid points */
  2228.  
  2229.     /* preliminary count of points inside array */
  2230.     goodcount = 0;
  2231.     for (i = 0; i < plot->p_count; i++)
  2232.     if (plot->points[i].type == INRANGE ||
  2233.         plot->points[i].type == OUTRANGE)
  2234.         ++goodcount;
  2235.     if (goodcount < 2)
  2236.     return;            /* cannot plot less than 2 points */
  2237.  
  2238.     gl = (int *) gp_alloc(goodcount * sizeof(int), "histeps valid point mapping");
  2239.     if (gl == NULL)
  2240.     return;
  2241.  
  2242. /* fill gl array with indexes of valid (non-undefined) points.  */
  2243.     goodcount = 0;
  2244.     for (i = 0; i < plot->p_count; i++)
  2245.     if (plot->points[i].type == INRANGE ||
  2246.         plot->points[i].type == OUTRANGE) {
  2247.         gl[goodcount] = i;
  2248.         ++goodcount;
  2249.     }
  2250. /* sort the data */
  2251.     for (bigi = i = 1; i < goodcount;) {
  2252.     if (plot->points[gl[i]].x < plot->points[gl[i - 1]].x) {
  2253.         hold = gl[i];
  2254.         gl[i] = gl[i - 1];
  2255.         gl[i - 1] = hold;
  2256.         if (i > 1) {
  2257.         i--;
  2258.         continue;
  2259.         }
  2260.     }
  2261.     i = ++bigi;
  2262.     }
  2263.  
  2264.     x = (3.0 * plot->points[gl[0]].x - plot->points[gl[1]].x) / 2.0;
  2265.     y = 0.0;
  2266.  
  2267.     xl = map_x(x);
  2268.     yl = map_y(y);
  2269.     (*t->move) (xl, yl);
  2270.  
  2271.     for (i = 0; i < goodcount - 1; i++) {    /* loop over all points except last  */
  2272.  
  2273.     yn = plot->points[gl[i]].y;
  2274.     xn = (plot->points[gl[i]].x + plot->points[gl[i + 1]].x) / 2.0;
  2275.     histeps_vertical(&xl, &yl, x, y, yn);
  2276.     histeps_horizontal(&xl, &yl, x, xn, yn);
  2277.  
  2278.     x = xn;
  2279.     y = yn;
  2280.     }
  2281.  
  2282.     yn = plot->points[gl[i]].y;
  2283.     xn = (3.0 * plot->points[gl[i]].x - plot->points[gl[i - 1]].x) / 2.0;
  2284.     histeps_vertical(&xl, &yl, x, y, yn);
  2285.     histeps_horizontal(&xl, &yl, x, xn, yn);
  2286.     histeps_vertical(&xl, &yl, xn, yn, 0.0);
  2287.  
  2288.     free(gl);
  2289. }
  2290. /* CAC 
  2291.  * Draw vertical line for the histeps routine.
  2292.  * Performs clipping.
  2293.  */
  2294. static void histeps_vertical(xl, yl, x, y1, y2)
  2295. int *xl, *yl;            /* keeps track of "cursor" position */
  2296. double x, y1, y2;        /* coordinates of vertical line */
  2297. {
  2298.     struct termentry *t = term;
  2299.     /* global x_min, x_max, y_min, y_max */
  2300.     int xm, y1m, y2m;
  2301.  
  2302.     if ((y1 < y_min && y2 < y_min) ||
  2303.     (y1 > y_max && y2 > y_max) ||
  2304.     x < x_min ||
  2305.     x > x_max)
  2306.     return;
  2307.  
  2308.     if (y1 < y_min)
  2309.     y1 = y_min;
  2310.     if (y1 > y_max)
  2311.     y1 = y_max;
  2312.     if (y2 < y_min)
  2313.     y2 = y_min;
  2314.     if (y2 > y_max)
  2315.     y2 = y_max;
  2316.  
  2317.     xm = map_x(x);
  2318.     y1m = map_y(y1);
  2319.     y2m = map_y(y2);
  2320.  
  2321.     if (y1m != *yl || xm != *xl)
  2322.     (*t->move) (xm, y1m);
  2323.     (*t->vector) (xm, y2m);
  2324.     *xl = xm;
  2325.     *yl = y2m;
  2326.  
  2327.     return;
  2328. }
  2329. /* CAC 
  2330.  * Draw horizontal line for the histeps routine.
  2331.  * Performs clipping.
  2332.  */
  2333. static void histeps_horizontal(xl, yl, x1, x2, y)
  2334. int *xl, *yl;            /* keeps track of "cursor" position */
  2335. double x1, x2, y;        /* coordinates of vertical line */
  2336. {
  2337.     struct termentry *t = term;
  2338.     /* global x_min, x_max, y_min, y_max */
  2339.     int x1m, x2m, ym;
  2340.  
  2341.     if ((x1 < x_min && x2 < x_min) ||
  2342.     (x1 > x_max && x2 > x_max) ||
  2343.     y < y_min ||
  2344.     y > y_max)
  2345.     return;
  2346.  
  2347.     if (x1 < x_min)
  2348.     x1 = x_min;
  2349.     if (x1 > x_max)
  2350.     x1 = x_max;
  2351.     if (x2 < x_min)
  2352.     x2 = x_min;
  2353.     if (x2 > x_max)
  2354.     x2 = x_max;
  2355.  
  2356.     ym = map_y(y);
  2357.     x1m = map_x(x1);
  2358.     x2m = map_x(x2);
  2359.  
  2360.     if (x1m != *xl || ym != *yl)
  2361.     (*t->move) (x1m, ym);
  2362.     (*t->vector) (x2m, ym);
  2363.     *xl = x2m;
  2364.     *yl = ym;
  2365.  
  2366.     return;
  2367. }
  2368.  
  2369.  
  2370. /* plot_bars:
  2371.  * Plot the curves in ERRORBARS style
  2372.  *  we just plot the bars; the points are plotted in plot_points
  2373.  */
  2374. static void plot_bars(plot)
  2375. struct curve_points *plot;
  2376. {
  2377.     int i;            /* point index */
  2378.     struct termentry *t = term;
  2379.     double x, y;        /* position of the bar */
  2380.     double ylow, yhigh;        /* the ends of the bars */
  2381.     double xlow, xhigh;
  2382.     double x1, y1, x2, y2, slope;    /* parameters for polar error bars */
  2383.     unsigned int xM, ylowM, yhighM;    /* the mapped version of above */
  2384.     unsigned int yM, xlowM, xhighM;
  2385.     TBOOLEAN low_inrange, high_inrange;
  2386.     int tic = ERRORBARTIC;
  2387.  
  2388. /* Limitation: no boxes with x errorbars */
  2389.  
  2390.     if ((plot->plot_style == YERRORBARS) || (plot->plot_style == XYERRORBARS) ||
  2391.     (plot->plot_style == BOXERROR)) {
  2392. /* Draw the vertical part of the bar */
  2393.     for (i = 0; i < plot->p_count; i++) {
  2394.         /* undefined points don't count */
  2395.         if (plot->points[i].type == UNDEFINED)
  2396.         continue;
  2397.  
  2398.         /* check to see if in xrange */
  2399.         x = plot->points[i].x;
  2400.         if (!inrange(x, x_min, x_max))
  2401.         continue;
  2402.         xM = map_x(x);
  2403.  
  2404.         /* check to see if in yrange */
  2405.         y = plot->points[i].y;
  2406.         if (!inrange(y, y_min, y_max))
  2407.         continue;
  2408.         yM = map_y(y);
  2409.  
  2410.         /* find low and high points of bar, and check yrange */
  2411.         yhigh = plot->points[i].yhigh;
  2412.         ylow = plot->points[i].ylow;
  2413.  
  2414.         high_inrange = inrange(yhigh, y_min, y_max);
  2415.         low_inrange = inrange(ylow, y_min, y_max);
  2416.  
  2417.         /* compute the plot position of yhigh */
  2418.         if (high_inrange)
  2419.         yhighM = map_y(yhigh);
  2420.         else if (samesign(yhigh - y_max, y_max - y_min))
  2421.         yhighM = map_y(y_max);
  2422.         else
  2423.         yhighM = map_y(y_min);
  2424.  
  2425.         /* compute the plot position of ylow */
  2426.         if (low_inrange)
  2427.         ylowM = map_y(ylow);
  2428.         else if (samesign(ylow - y_max, y_max - y_min))
  2429.         ylowM = map_y(y_max);
  2430.         else
  2431.         ylowM = map_y(y_min);
  2432.  
  2433.         if (!high_inrange && !low_inrange && ylowM == yhighM)
  2434.         /* both out of range on the same side */
  2435.         continue;
  2436.  
  2437.         /* find low and high points of bar, and check xrange */
  2438.         xhigh = plot->points[i].xhigh;
  2439.         xlow = plot->points[i].xlow;
  2440.  
  2441.         high_inrange = inrange(xhigh, x_min, x_max);
  2442.         low_inrange = inrange(xlow, x_min, x_max);
  2443.  
  2444.         /* compute the plot position of xhigh */
  2445.         if (high_inrange)
  2446.         xhighM = map_x(xhigh);
  2447.         else if (samesign(xhigh - x_max, x_max - x_min))
  2448.         xhighM = map_x(x_max);
  2449.         else
  2450.         xhighM = map_x(x_min);
  2451.  
  2452.         /* compute the plot position of xlow */
  2453.         if (low_inrange)
  2454.         xlowM = map_x(xlow);
  2455.         else if (samesign(xlow - x_max, x_max - x_min))
  2456.         xlowM = map_x(x_max);
  2457.         else
  2458.         xlowM = map_x(x_min);
  2459.  
  2460.         if (!high_inrange && !low_inrange && xlowM == xhighM)
  2461.         /* both out of range on the same side */
  2462.         continue;
  2463.  
  2464.         /* by here everything has been mapped */
  2465.         if (!polar) {
  2466.         /* HBB 981130: use Igor's routine *only* for polar errorbars */
  2467.         (*t->move) (xM, ylowM);
  2468.         /* draw the main bar */
  2469.         (*t->vector) (xM, yhighM);
  2470.         if (bar_size > 0.0) {
  2471.             /* draw the bottom tic */
  2472.             (*t->move) ((unsigned int) (xM - bar_size * tic), ylowM);
  2473.             (*t->vector) ((unsigned int) (xM + bar_size * tic), ylowM);
  2474.             /* draw the top tic */
  2475.             (*t->move) ((unsigned int) (xM - bar_size * tic), yhighM);
  2476.             (*t->vector) ((unsigned int) (xM + bar_size * tic), yhighM);
  2477.         }
  2478.         } else {
  2479.         /* HBB 981130: see above */
  2480.         /* The above has been replaced by Igor inorder to get errorbars
  2481.            coming out in polar mode AND to stop the bar from going
  2482.            through the symbol */
  2483.         if ((xhighM - xlowM) * (xhighM - xlowM) + (yhighM - ylowM) * (yhighM - ylowM)
  2484.             > pointsize * tic * pointsize * tic * 4.5) {
  2485.             /* Only plot the error bar if it is bigger than the
  2486.              * symbol */
  2487.             /* The factor of 4.5 should strictly be 4.0, but it looks
  2488.              * better to drop the error bar if it is only slightly
  2489.              * bigger than the symbol, Igor. */
  2490.             if (xlowM == xhighM) {
  2491.             (*t->move) (xM, ylowM);
  2492.             /* draw the main bar to the symbol end */
  2493.             (*t->vector) (xM, (unsigned int) (yM - pointsize * tic));
  2494.             (*t->move) (xM, (unsigned int) (yM + pointsize * tic));
  2495.             /* draw the other part of the main bar */
  2496.             (*t->vector) (xM, yhighM);
  2497.             } else {
  2498.             (*t->move) (xlowM, ylowM);
  2499.             /* draw the main bar in polar mode. Note that here
  2500.              * the bar is drawn through the symbol. I tried to
  2501.              * fix this, but got into trouble with the two bars
  2502.              * (on either side of symbol) not being perfectly
  2503.              * parallel due to mapping considerations. Igor
  2504.              */
  2505.             (*t->vector) (xhighM, yhighM);
  2506.             }
  2507.             if (bar_size > 0.0) {
  2508.             /* The following attempts to ensure that the tics
  2509.              * are perpendicular to the error bar, Igor. */
  2510.             /*perpendicular to the main bar */
  2511.             slope = (xlowM * 1.0 - xhighM * 1.0) / (yhighM * 1.0 - ylowM * 1.0 + 1e-10);
  2512.             x1 = xlowM + bar_size * tic / sqrt(1.0 + slope * slope);
  2513.             x2 = xlowM - bar_size * tic / sqrt(1.0 + slope * slope);
  2514.             y1 = slope * (x1 - xlowM) + ylowM;
  2515.             y2 = slope * (x2 - xlowM) + ylowM;
  2516.  
  2517.             /* draw the bottom tic */
  2518.             (*t->move) ((unsigned int) x1, (unsigned int) y1);
  2519.             (*t->vector) ((unsigned int) x2, (unsigned int) y2);
  2520.  
  2521.             x1 = xhighM + bar_size * tic / sqrt(1.0 + slope * slope);
  2522.             x2 = xhighM - bar_size * tic / sqrt(1.0 + slope * slope);
  2523.             y1 = slope * (x1 - xhighM) + yhighM;
  2524.             y2 = slope * (x2 - xhighM) + yhighM;
  2525.             /* draw the top tic */
  2526.             (*t->move) ((unsigned int) x1, (unsigned int) y1);
  2527.             (*t->vector) ((unsigned int) x2, (unsigned int) y2);
  2528.             }        /* if error bar is bigger than symbol */
  2529.         }
  2530.         }            /* HBB 981130: see above */
  2531.     }            /* for loop */
  2532.     }                /* if yerrorbars OR xyerrorbars */
  2533.     if ((plot->plot_style == XERRORBARS) || (plot->plot_style == XYERRORBARS)) {
  2534.  
  2535. /* Draw the horizontal part of the bar */
  2536.     for (i = 0; i < plot->p_count; i++) {
  2537.         /* undefined points don't count */
  2538.         if (plot->points[i].type == UNDEFINED)
  2539.         continue;
  2540.  
  2541.         /* check to see if in yrange */
  2542.         y = plot->points[i].y;
  2543.         if (!inrange(y, y_min, y_max))
  2544.         continue;
  2545.         yM = map_y(y);
  2546.  
  2547.         /* find low and high points of bar, and check xrange */
  2548.         xhigh = plot->points[i].xhigh;
  2549.         xlow = plot->points[i].xlow;
  2550.  
  2551.         high_inrange = inrange(xhigh, x_min, x_max);
  2552.         low_inrange = inrange(xlow, x_min, x_max);
  2553.  
  2554.         /* compute the plot position of xhigh */
  2555.         if (high_inrange)
  2556.         xhighM = map_x(xhigh);
  2557.         else if (samesign(xhigh - x_max, x_max - x_min))
  2558.         xhighM = map_x(x_max);
  2559.         else
  2560.         xhighM = map_x(x_min);
  2561.  
  2562.         /* compute the plot position of xlow */
  2563.         if (low_inrange)
  2564.         xlowM = map_x(xlow);
  2565.         else if (samesign(xlow - x_max, x_max - x_min))
  2566.         xlowM = map_x(x_max);
  2567.         else
  2568.         xlowM = map_x(x_min);
  2569.  
  2570.         if (!high_inrange && !low_inrange && xlowM == xhighM)
  2571.         /* both out of range on the same side */
  2572.         continue;
  2573.  
  2574.         /* by here everything has been mapped */
  2575.         (*t->move) (xlowM, yM);
  2576.         (*t->vector) (xhighM, yM);    /* draw the main bar */
  2577.         if (bar_size > 0.0) {
  2578.         (*t->move) (xlowM, (unsigned int) (yM - bar_size * tic));    /* draw the left tic */
  2579.         (*t->vector) (xlowM, (unsigned int) (yM + bar_size * tic));
  2580.         (*t->move) (xhighM, (unsigned int) (yM - bar_size * tic));    /* draw the right tic */
  2581.         (*t->vector) (xhighM, (unsigned int) (yM + bar_size * tic));
  2582.         }
  2583.     }            /* for loop */
  2584.     }                /* if xerrorbars OR xyerrorbars */
  2585. }
  2586.  
  2587. /* plot_boxes:
  2588.  * Plot the curves in BOXES style
  2589.  */
  2590. static void plot_boxes(plot, xaxis_y)
  2591. struct curve_points *plot;
  2592. int xaxis_y;
  2593. {
  2594.     int i;            /* point index */
  2595.     int xl, xr, yt;        /* point in terminal coordinates */
  2596.     double dxl, dxr, dyt;
  2597.     struct termentry *t = term;
  2598.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2599.     TBOOLEAN boxxy = (plot->plot_style == BOXXYERROR);
  2600.  
  2601.     for (i = 0; i < plot->p_count; i++) {
  2602.  
  2603.     switch (plot->points[i].type) {
  2604.     case OUTRANGE:
  2605.     case INRANGE:{
  2606.         if (plot->points[i].z < 0.0) {
  2607.             /* need to auto-calc width */
  2608.             /* ASSERT(boxwidth <= 0.0); - else graphics.c
  2609.              * provides width */
  2610.  
  2611.             /* calculate width */
  2612.             if (prev != UNDEFINED)
  2613.             dxl = (plot->points[i - 1].x - plot->points[i].x) / 2.0;
  2614.             else
  2615.             dxl = 0.0;
  2616.  
  2617.             if (i < plot->p_count - 1) {
  2618.             if (plot->points[i + 1].type != UNDEFINED)
  2619.                 dxr = (plot->points[i + 1].x - plot->points[i].x) / 2.0;
  2620.             else
  2621.                 dxr = -dxl;
  2622.             } else {
  2623.             dxr = -dxl;
  2624.             }
  2625.  
  2626.             if (prev == UNDEFINED)
  2627.             dxl = -dxr;
  2628.  
  2629.             dxl = plot->points[i].x + dxl;
  2630.             dxr = plot->points[i].x + dxr;
  2631.         } else {    /* z >= 0 */
  2632.             dxr = plot->points[i].xhigh;
  2633.             dxl = plot->points[i].xlow;
  2634.         }
  2635.  
  2636.         if (boxxy) {
  2637.             dyt = plot->points[i].yhigh;
  2638.             xaxis_y = map_y(plot->points[i].ylow);
  2639.         } else {
  2640.             dyt = plot->points[i].y;
  2641.         }
  2642.  
  2643.         /* clip to border */
  2644.         if ((y_min < y_max && dyt < y_min)
  2645.             || (y_max < y_min && dyt > y_min))
  2646.             dyt = y_min;
  2647.         if ((y_min < y_max && dyt > y_max)
  2648.             || (y_max < y_min && dyt < y_max))
  2649.             dyt = y_max;
  2650.         if ((x_min < x_max && dxr < x_min)
  2651.             || (x_max < x_min && dxr > x_min))
  2652.             dxr = x_min;
  2653.         if ((x_min < x_max && dxr > x_max)
  2654.             || (x_max < x_min && dxr < x_max))
  2655.             dxr = x_max;
  2656.         if ((x_min < x_max && dxl < x_min)
  2657.             || (x_max < x_min && dxl > x_min))
  2658.             dxl = x_min;
  2659.         if ((x_min < x_max && dxl > x_max)
  2660.             || (x_max < x_min && dxl < x_max))
  2661.             dxl = x_max;
  2662.  
  2663.         xl = map_x(dxl);
  2664.         xr = map_x(dxr);
  2665.         yt = map_y(dyt);
  2666.  
  2667.         (*t->move) (xl, xaxis_y);
  2668.         (*t->vector) (xl, yt);
  2669.         (*t->vector) (xr, yt);
  2670.         (*t->vector) (xr, xaxis_y);
  2671.         (*t->vector) (xl, xaxis_y);
  2672.         break;
  2673.         }            /* case OUTRANGE, INRANGE */
  2674.  
  2675.     default:        /* just a safety */
  2676.     case UNDEFINED:{
  2677.         break;
  2678.         }
  2679.  
  2680.     }            /* switch point-type */
  2681.  
  2682.     prev = plot->points[i].type;
  2683.  
  2684.     }                /*loop */
  2685. }
  2686.  
  2687.  
  2688.  
  2689. /* plot_points:
  2690.  * Plot the curves in POINTSTYLE style
  2691.  */
  2692. static void plot_points(plot)
  2693. struct curve_points *plot;
  2694. {
  2695.     int i;
  2696.     int x, y;
  2697.     struct termentry *t = term;
  2698.  
  2699.     for (i = 0; i < plot->p_count; i++) {
  2700.     if (plot->points[i].type == INRANGE) {
  2701.         x = map_x(plot->points[i].x);
  2702.         y = map_y(plot->points[i].y);
  2703.         /* do clipping if necessary */
  2704.         if (!clip_points ||
  2705.         (x >= xleft + p_width && y >= ybot + p_height
  2706.          && x <= xright - p_width && y <= ytop - p_height))
  2707.         (*t->point) (x, y, plot->lp_properties.p_type);
  2708.     }
  2709.     }
  2710. }
  2711.  
  2712. /* plot_dots:
  2713.  * Plot the curves in DOTS style
  2714.  */
  2715. static void plot_dots(plot)
  2716. struct curve_points *plot;
  2717. {
  2718.     int i;
  2719.     int x, y;
  2720.     struct termentry *t = term;
  2721.  
  2722.     for (i = 0; i < plot->p_count; i++) {
  2723.     if (plot->points[i].type == INRANGE) {
  2724.         x = map_x(plot->points[i].x);
  2725.         y = map_y(plot->points[i].y);
  2726.         /* point type -1 is a dot */
  2727.         (*t->point) (x, y, -1);
  2728.     }
  2729.     }
  2730. }
  2731.  
  2732. /* plot_vectors:
  2733.  * Plot the curves in VECTORS style
  2734.  */
  2735. static void plot_vectors(plot)
  2736. struct curve_points *plot;
  2737. {
  2738.     int i;
  2739.     int x1, y1, x2, y2;
  2740.     struct termentry *t = term;
  2741.  
  2742.     for (i = 0; i < plot->p_count; i++) {
  2743.     if (plot->points[i].type == INRANGE) {
  2744.         x1 = map_x(plot->points[i].xlow);
  2745.         y1 = map_y(plot->points[i].ylow);
  2746.         x2 = map_x(plot->points[i].xhigh);
  2747.         y2 = map_y(plot->points[i].yhigh);
  2748.         (*t->arrow) (x1, y1, x2, y2, TRUE);
  2749.     }
  2750.     }
  2751. }
  2752.  
  2753.  
  2754. /* plot_f_bars() - finance bars */
  2755. static void plot_f_bars(plot)
  2756. struct curve_points *plot;
  2757. {
  2758.     int i;            /* point index */
  2759.     struct termentry *t = term;
  2760.     double x;            /* position of the bar */
  2761.     double ylow, yhigh, yclose, yopen;    /* the ends of the bars */
  2762.     unsigned int xM, ylowM, yhighM;    /* the mapped version of above */
  2763.     TBOOLEAN low_inrange, high_inrange;
  2764.     int tic = ERRORBARTIC / 2;
  2765.  
  2766.     for (i = 0; i < plot->p_count; i++) {
  2767.     /* undefined points don't count */
  2768.     if (plot->points[i].type == UNDEFINED)
  2769.         continue;
  2770.  
  2771.     /* check to see if in xrange */
  2772.     x = plot->points[i].x;
  2773.     if (!inrange(x, x_min, x_max))
  2774.         continue;
  2775.     xM = map_x(x);
  2776.  
  2777.     /* find low and high points of bar, and check yrange */
  2778.     yhigh = plot->points[i].yhigh;
  2779.     ylow = plot->points[i].ylow;
  2780.     yclose = plot->points[i].z;
  2781.     yopen = plot->points[i].y;
  2782.  
  2783.     high_inrange = inrange(yhigh, y_min, y_max);
  2784.     low_inrange = inrange(ylow, y_min, y_max);
  2785.  
  2786.     /* compute the plot position of yhigh */
  2787.     if (high_inrange)
  2788.         yhighM = map_y(yhigh);
  2789.     else if (samesign(yhigh - y_max, y_max - y_min))
  2790.         yhighM = map_y(y_max);
  2791.     else
  2792.         yhighM = map_y(y_min);
  2793.  
  2794.     /* compute the plot position of ylow */
  2795.     if (low_inrange)
  2796.         ylowM = map_y(ylow);
  2797.     else if (samesign(ylow - y_max, y_max - y_min))
  2798.         ylowM = map_y(y_max);
  2799.     else
  2800.         ylowM = map_y(y_min);
  2801.  
  2802.     if (!high_inrange && !low_inrange && ylowM == yhighM)
  2803.         /* both out of range on the same side */
  2804.         continue;
  2805.  
  2806.     /* by here everything has been mapped */
  2807.     (*t->move) (xM, ylowM);
  2808.     (*t->vector) (xM, yhighM);    /* draw the main bar */
  2809.     /* draw the open tic */
  2810.     (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2811.     (*t->vector) (xM, map_y(yopen));
  2812.     /* draw the close tic */
  2813.     (*t->move) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
  2814.     (*t->vector) (xM, map_y(yclose));
  2815.     }
  2816. }
  2817.  
  2818.  
  2819. /* plot_c_bars:
  2820.  * Plot the curves in CANDLESTICSK style
  2821.  *  we just plot the bars; the points are not plotted 
  2822.  */
  2823. static void plot_c_bars(plot)
  2824. struct curve_points *plot;
  2825. {
  2826.     int i;            /* point index */
  2827.     struct termentry *t = term;
  2828.     double x;            /* position of the bar */
  2829.     double ylow, yhigh, yclose, yopen;    /* the ends of the bars */
  2830.     unsigned int xM, ylowM, yhighM;    /* the mapped version of above */
  2831.     TBOOLEAN low_inrange, high_inrange;
  2832.     int tic = ERRORBARTIC / 2;
  2833.  
  2834.     for (i = 0; i < plot->p_count; i++) {
  2835.     /* undefined points don't count */
  2836.     if (plot->points[i].type == UNDEFINED)
  2837.         continue;
  2838.  
  2839.     /* check to see if in xrange */
  2840.     x = plot->points[i].x;
  2841.     if (!inrange(x, x_min, x_max))
  2842.         continue;
  2843.     xM = map_x(x);
  2844.  
  2845.     /* find low and high points of bar, and check yrange */
  2846.     yhigh = plot->points[i].yhigh;
  2847.     ylow = plot->points[i].ylow;
  2848.     yclose = plot->points[i].z;
  2849.     yopen = plot->points[i].y;
  2850.  
  2851.     high_inrange = inrange(yhigh, y_min, y_max);
  2852.     low_inrange = inrange(ylow, y_min, y_max);
  2853.  
  2854.     /* compute the plot position of yhigh */
  2855.     if (high_inrange)
  2856.         yhighM = map_y(yhigh);
  2857.     else if (samesign(yhigh - y_max, y_max - y_min))
  2858.         yhighM = map_y(y_max);
  2859.     else
  2860.         yhighM = map_y(y_min);
  2861.  
  2862.     /* compute the plot position of ylow */
  2863.     if (low_inrange)
  2864.         ylowM = map_y(ylow);
  2865.     else if (samesign(ylow - y_max, y_max - y_min))
  2866.         ylowM = map_y(y_max);
  2867.     else
  2868.         ylowM = map_y(y_min);
  2869.  
  2870.     if (!high_inrange && !low_inrange && ylowM == yhighM)
  2871.         /* both out of range on the same side */
  2872.         continue;
  2873.  
  2874.     /* by here everything has been mapped */
  2875.     if (yopen <= yclose) {
  2876.         (*t->move) (xM, ylowM);
  2877.         (*t->vector) (xM, map_y(yopen));    /* draw the lower bar */
  2878.         /* draw the open tic */
  2879.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2880.         /* draw the open tic */
  2881.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
  2882.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
  2883.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
  2884.         /* draw the open tic */
  2885.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2886.         (*t->move) (xM, map_y(yclose));    /* draw the close tic */
  2887.         (*t->vector) (xM, yhighM);
  2888.     } else {
  2889.         (*t->move) (xM, ylowM);
  2890.         (*t->vector) (xM, yhighM);
  2891.         /* draw the open tic */
  2892.         (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2893.         /* draw the open tic */
  2894.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
  2895.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
  2896.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
  2897.         /* draw the open tic */
  2898.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2899.         /* draw the close tic */
  2900.         (*t->move) ((unsigned int) (xM - bar_size * tic / 2), map_y(yclose));
  2901.         /* draw the open tic */
  2902.         (*t->vector) ((unsigned int) (xM - bar_size * tic / 2), map_y(yopen));
  2903.         /* draw the close tic */
  2904.         (*t->move) ((unsigned int) (xM + bar_size * tic / 2), map_y(yclose));
  2905.         /* draw the open tic */
  2906.         (*t->vector) ((unsigned int) (xM + bar_size * tic / 2), map_y(yopen));
  2907.     }
  2908.  
  2909.     }
  2910. }
  2911.  
  2912. /* single edge intersection algorithm */
  2913. /* Given two points, one inside and one outside the plot, return
  2914.  * the point where an edge of the plot intersects the line segment defined 
  2915.  * by the two points.
  2916.  */
  2917. static void edge_intersect(points, i, ex, ey)
  2918. struct coordinate GPHUGE *points;    /* the points array */
  2919. int i;                /* line segment from point i-1 to point i */
  2920. double *ex, *ey;        /* the point where it crosses an edge */
  2921. {
  2922.     /* global x_min, x_max, y_min, x_max */
  2923.     double ix = points[i - 1].x;
  2924.     double iy = points[i - 1].y;
  2925.     double ox = points[i].x;
  2926.     double oy = points[i].y;
  2927.     double x, y;        /* possible intersection point */
  2928.  
  2929.     if (points[i].type == INRANGE) {
  2930.     /* swap points around so that ix/ix/iz are INRANGE and
  2931.      * ox/oy/oz are OUTRANGE
  2932.      */
  2933.     x = ix;
  2934.     ix = ox;
  2935.     ox = x;
  2936.     y = iy;
  2937.     iy = oy;
  2938.     oy = y;
  2939.     }
  2940.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  2941.      * cope with them here, so don't process them as a "real" OUTRANGE point 
  2942.      *
  2943.      * If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  2944.      * so drop out by returning the INRANGE point. 
  2945.      *
  2946.      * Obviously, only need to test the OUTRANGE point (coordinates) */
  2947.     if (ox == -VERYLARGE || oy == -VERYLARGE) {
  2948.     *ex = ix;
  2949.     *ey = iy;
  2950.  
  2951.     if (ox == -VERYLARGE) {
  2952.         /* can't get a direction to draw line, so simply
  2953.          * return INRANGE point */
  2954.         if (oy == -VERYLARGE)
  2955.         return;
  2956.  
  2957.         *ex = x_min;
  2958.         return;
  2959.     }
  2960.     /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
  2961.     *ey = y_min;
  2962.     return;
  2963.     }
  2964.     /*
  2965.      * Can't have case (ix == ox && iy == oy) as one point
  2966.      * is INRANGE and one point is OUTRANGE.
  2967.      */
  2968.     if (iy == oy) {
  2969.     /* horizontal line */
  2970.     /* assume inrange(iy, y_min, y_max) */
  2971.     *ey = iy;        /* == oy */
  2972.  
  2973.     if (inrange(x_max, ix, ox))
  2974.         *ex = x_max;
  2975.     else if (inrange(x_min, ix, ox))
  2976.         *ex = x_min;
  2977.     else {
  2978.         graph_error("error in edge_intersect");
  2979.     }
  2980.     return;
  2981.     } else if (ix == ox) {
  2982.     /* vertical line */
  2983.     /* assume inrange(ix, x_min, x_max) */
  2984.     *ex = ix;        /* == ox */
  2985.  
  2986.     if (inrange(y_max, iy, oy))
  2987.         *ey = y_max;
  2988.     else if (inrange(y_min, iy, oy))
  2989.         *ey = y_min;
  2990.     else {
  2991.         graph_error("error in edge_intersect");
  2992.     }
  2993.     return;
  2994.     }
  2995.     /* slanted line of some kind */
  2996.  
  2997.     /* does it intersect y_min edge */
  2998.     if (inrange(y_min, iy, oy) && y_min != iy && y_min != oy) {
  2999.     x = ix + (y_min - iy) * ((ox - ix) / (oy - iy));
  3000.     if (inrange(x, x_min, x_max)) {
  3001.         *ex = x;
  3002.         *ey = y_min;
  3003.         return;        /* yes */
  3004.     }
  3005.     }
  3006.     /* does it intersect y_max edge */
  3007.     if (inrange(y_max, iy, oy) && y_max != iy && y_max != oy) {
  3008.     x = ix + (y_max - iy) * ((ox - ix) / (oy - iy));
  3009.     if (inrange(x, x_min, x_max)) {
  3010.         *ex = x;
  3011.         *ey = y_max;
  3012.         return;        /* yes */
  3013.     }
  3014.     }
  3015.     /* does it intersect x_min edge */
  3016.     if (inrange(x_min, ix, ox) && x_min != ix && x_min != ox) {
  3017.     y = iy + (x_min - ix) * ((oy - iy) / (ox - ix));
  3018.     if (inrange(y, y_min, y_max)) {
  3019.         *ex = x_min;
  3020.         *ey = y;
  3021.         return;
  3022.     }
  3023.     }
  3024.     /* does it intersect x_max edge */
  3025.     if (inrange(x_max, ix, ox) && x_max != ix && x_max != ox) {
  3026.     y = iy + (x_max - ix) * ((oy - iy) / (ox - ix));
  3027.     if (inrange(y, y_min, y_max)) {
  3028.         *ex = x_max;
  3029.         *ey = y;
  3030.         return;
  3031.     }
  3032.     }
  3033.     /* If we reach here, the inrange point is on the edge, and
  3034.      * the line segment from the outrange point does not cross any 
  3035.      * other edges to get there. In this case, we return the inrange 
  3036.      * point as the 'edge' intersection point. This will basically draw
  3037.      * line.
  3038.      */
  3039.     *ex = ix;
  3040.     *ey = iy;
  3041.     return;
  3042. }
  3043.  
  3044. /* XXX - JG  */
  3045. /* single edge intersection algorithm for "steps" curves */
  3046. /* 
  3047.  * Given two points, one inside and one outside the plot, return
  3048.  * the point where an edge of the plot intersects the line segments
  3049.  * forming the step between the two points. 
  3050.  *
  3051.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3052.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and 
  3053.  * (x2,y1)->(x2,y2). 
  3054.  */
  3055. static void edge_intersect_steps(points, i, ex, ey)
  3056. struct coordinate GPHUGE *points;    /* the points array */
  3057. int i;                /* line segment from point i-1 to point i */
  3058. double *ex, *ey;        /* the point where it crosses an edge */
  3059. {
  3060.     /* global x_min, x_max, y_min, x_max */
  3061.     double ax = points[i - 1].x;
  3062.     double ay = points[i - 1].y;
  3063.     double bx = points[i].x;
  3064.     double by = points[i].y;
  3065.  
  3066.     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
  3067.     if (inrange(ay, y_min, y_max)) {
  3068.         *ey = ay;
  3069.         if (ax > x_max)
  3070.         *ex = x_max;
  3071.         else        /* x < x_min */
  3072.         *ex = x_min;
  3073.     } else {
  3074.         *ex = bx;
  3075.         if (ay > y_max)
  3076.         *ey = y_max;
  3077.         else        /* y < y_min */
  3078.         *ey = y_min;
  3079.     }
  3080.     } else {            /* from INRANGE to OUTRANGE */
  3081.     if (inrange(bx, x_min, x_max)) {
  3082.         *ex = bx;
  3083.         if (by > y_max)
  3084.         *ey = y_max;
  3085.         else        /* y < y_min */
  3086.         *ey = y_min;
  3087.     } else {
  3088.         *ey = ay;
  3089.         if (bx > x_max)
  3090.         *ex = x_max;
  3091.         else        /* x < x_min */
  3092.         *ex = x_min;
  3093.     }
  3094.     }
  3095.     return;
  3096. }
  3097.  
  3098. /* XXX - HOE  */
  3099. /* single edge intersection algorithm for "fsteps" curves */
  3100. /* fsteps means step on forward y-value. 
  3101.  * Given two points, one inside and one outside the plot, return
  3102.  * the point where an edge of the plot intersects the line segments
  3103.  * forming the step between the two points. 
  3104.  *
  3105.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3106.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and 
  3107.  * (x1,y2)->(x2,y2). 
  3108.  */
  3109. static void edge_intersect_fsteps(points, i, ex, ey)
  3110. struct coordinate GPHUGE *points;    /* the points array */
  3111. int i;                /* line segment from point i-1 to point i */
  3112. double *ex, *ey;        /* the point where it crosses an edge */
  3113. {
  3114.     /* global x_min, x_max, y_min, x_max */
  3115.     double ax = points[i - 1].x;
  3116.     double ay = points[i - 1].y;
  3117.     double bx = points[i].x;
  3118.     double by = points[i].y;
  3119.  
  3120.     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
  3121.     if (inrange(ax, x_min, x_max)) {
  3122.         *ex = ax;
  3123.         if (ay > y_max)
  3124.         *ey = y_max;
  3125.         else        /* y < y_min */
  3126.         *ey = y_min;
  3127.     } else {
  3128.         *ey = by;
  3129.         if (bx > x_max)
  3130.         *ex = x_max;
  3131.         else        /* x < x_min */
  3132.         *ex = x_min;
  3133.     }
  3134.     } else {            /* from INRANGE to OUTRANGE */
  3135.     if (inrange(by, y_min, y_max)) {
  3136.         *ey = by;
  3137.         if (bx > x_max)
  3138.         *ex = x_max;
  3139.         else        /* x < x_min */
  3140.         *ex = x_min;
  3141.     } else {
  3142.         *ex = ax;
  3143.         if (by > y_max)
  3144.         *ey = y_max;
  3145.         else        /* y < y_min */
  3146.         *ey = y_min;
  3147.     }
  3148.     }
  3149.     return;
  3150. }
  3151.  
  3152. /* XXX - JG  */
  3153. /* double edge intersection algorithm for "steps" plot */
  3154. /* Given two points, both outside the plot, return the points where an 
  3155.  * edge of the plot intersects the line segments forming a step 
  3156.  * by the two points. There may be zero, one, two, or an infinite number
  3157.  * of intersection points. (One means an intersection at a corner, infinite
  3158.  * means overlaying the edge itself). We return FALSE when there is nothing
  3159.  * to draw (zero intersections), and TRUE when there is something to 
  3160.  * draw (the one-point case is a degenerate of the two-point case and we do 
  3161.  * not distinguish it - we draw it anyway).
  3162.  *
  3163.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3164.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and 
  3165.  * (x2,y1)->(x2,y2). 
  3166.  */
  3167. static TBOOLEAN /* any intersection? */
  3168.  two_edge_intersect_steps(points, i, lx, ly)
  3169. struct coordinate GPHUGE *points;    /* the points array */
  3170. int i;                /* line segment from point i-1 to point i */
  3171. double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  3172. {
  3173.     /* global x_min, x_max, y_min, x_max */
  3174.     double ax = points[i - 1].x;
  3175.     double ay = points[i - 1].y;
  3176.     double bx = points[i].x;
  3177.     double by = points[i].y;
  3178.  
  3179.     if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
  3180.     GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
  3181.     ((ay > y_max || ay < y_min) &&
  3182.      (bx > x_max || bx < x_min))) {
  3183.     return (FALSE);
  3184.     } else if (inrange(ay, y_min, y_max) && inrange(bx, x_min, x_max)) {    /* corner of step inside plotspace */
  3185.     *ly++ = ay;
  3186.     if (ax < x_min)
  3187.         *lx++ = x_min;
  3188.     else
  3189.         *lx++ = x_max;
  3190.  
  3191.     *lx++ = bx;
  3192.     if (by < y_min)
  3193.         *ly++ = y_min;
  3194.     else
  3195.         *ly++ = y_max;
  3196.  
  3197.     return (TRUE);
  3198.     } else if (inrange(ay, y_min, y_max)) {    /* cross plotspace in x-direction */
  3199.     *lx++ = x_min;
  3200.     *ly++ = ay;
  3201.     *lx++ = x_max;
  3202.     *ly++ = ay;
  3203.     return (TRUE);
  3204.     } else if (inrange(ax, x_min, x_max)) {    /* cross plotspace in y-direction */
  3205.     *lx++ = bx;
  3206.     *ly++ = y_min;
  3207.     *lx++ = bx;
  3208.     *ly++ = y_max;
  3209.     return (TRUE);
  3210.     } else
  3211.     return (FALSE);
  3212. }
  3213.  
  3214. /* XXX - HOE  */
  3215. /* double edge intersection algorithm for "fsteps" plot */
  3216. /* Given two points, both outside the plot, return the points where an 
  3217.  * edge of the plot intersects the line segments forming a step 
  3218.  * by the two points. There may be zero, one, two, or an infinite number
  3219.  * of intersection points. (One means an intersection at a corner, infinite
  3220.  * means overlaying the edge itself). We return FALSE when there is nothing
  3221.  * to draw (zero intersections), and TRUE when there is something to 
  3222.  * draw (the one-point case is a degenerate of the two-point case and we do 
  3223.  * not distinguish it - we draw it anyway).
  3224.  *
  3225.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3226.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and 
  3227.  * (x1,y2)->(x2,y2). 
  3228.  */
  3229. static TBOOLEAN /* any intersection? */
  3230.  two_edge_intersect_fsteps(points, i, lx, ly)
  3231. struct coordinate GPHUGE *points;    /* the points array */
  3232. int i;                /* line segment from point i-1 to point i */
  3233. double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  3234. {
  3235.     /* global x_min, x_max, y_min, x_max */
  3236.     double ax = points[i - 1].x;
  3237.     double ay = points[i - 1].y;
  3238.     double bx = points[i].x;
  3239.     double by = points[i].y;
  3240.  
  3241.     if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
  3242.     GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
  3243.     ((by > y_max || by < y_min) &&
  3244.      (ax > x_max || ax < x_min))) {
  3245.     return (FALSE);
  3246.     } else if (inrange(by, y_min, y_max) && inrange(ax, x_min, x_max)) {    /* corner of step inside plotspace */
  3247.     *lx++ = ax;
  3248.     if (ay < y_min)
  3249.         *ly++ = y_min;
  3250.     else
  3251.         *ly++ = y_max;
  3252.  
  3253.     *ly = by;
  3254.     if (bx < x_min)
  3255.         *lx = x_min;
  3256.     else
  3257.         *lx = x_max;
  3258.  
  3259.     return (TRUE);
  3260.     } else if (inrange(by, y_min, y_max)) {    /* cross plotspace in x-direction */
  3261.     *lx++ = x_min;
  3262.     *ly++ = by;
  3263.     *lx = x_max;
  3264.     *ly = by;
  3265.     return (TRUE);
  3266.     } else if (inrange(ax, x_min, x_max)) {    /* cross plotspace in y-direction */
  3267.     *lx++ = ax;
  3268.     *ly++ = y_min;
  3269.     *lx = ax;
  3270.     *ly = y_max;
  3271.     return (TRUE);
  3272.     } else
  3273.     return (FALSE);
  3274. }
  3275.  
  3276. /* double edge intersection algorithm */
  3277. /* Given two points, both outside the plot, return
  3278.  * the points where an edge of the plot intersects the line segment defined 
  3279.  * by the two points. There may be zero, one, two, or an infinite number
  3280.  * of intersection points. (One means an intersection at a corner, infinite
  3281.  * means overlaying the edge itself). We return FALSE when there is nothing
  3282.  * to draw (zero intersections), and TRUE when there is something to 
  3283.  * draw (the one-point case is a degenerate of the two-point case and we do 
  3284.  * not distinguish it - we draw it anyway).
  3285.  */
  3286. static TBOOLEAN /* any intersection? */
  3287.                                two_edge_intersect(points, i, lx, ly)
  3288. struct coordinate GPHUGE *points;    /* the points array */
  3289. int i;                /* line segment from point i-1 to point i */
  3290. double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  3291. {
  3292.     /* global x_min, x_max, y_min, x_max */
  3293.     int count;
  3294.     double ix = points[i - 1].x;
  3295.     double iy = points[i - 1].y;
  3296.     double ox = points[i].x;
  3297.     double oy = points[i].y;
  3298.     double t[4];
  3299.     double swap;
  3300.     double t_min, t_max;
  3301. #if 0
  3302.     fprintf(stderr, "\ntwo_edge_intersect (%g, %g) and (%g, %g) : ",
  3303.         points[i - 1].x, points[i - 1].y,
  3304.         points[i].x, points[i].y);
  3305. #endif
  3306.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  3307.        cope with them here, so don't process them as a "real" OUTRANGE point 
  3308.  
  3309.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  3310.        so drop out by returning FALSE */
  3311.  
  3312.     count = 0;
  3313.     if (ix == -VERYLARGE)
  3314.     count++;
  3315.     if (ox == -VERYLARGE)
  3316.     count++;
  3317.     if (iy == -VERYLARGE)
  3318.     count++;
  3319.     if (oy == -VERYLARGE)
  3320.     count++;
  3321.  
  3322.     /* either doesn't pass through graph area *or* 
  3323.        can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
  3324.     if (count > 1) {
  3325. #if 0
  3326.     fprintf(stderr, "\tA\n");
  3327. #endif
  3328.     return (FALSE);
  3329.     }
  3330.     if (ox == -VERYLARGE || ix == -VERYLARGE) {
  3331.     if (ix == -VERYLARGE) {
  3332.         /* swap points so ix/iy don't have a -VERYLARGE component */
  3333.         swap = ix;
  3334.         ix = ox;
  3335.         ox = swap;
  3336.         swap = iy;
  3337.         iy = oy;
  3338.         oy = swap;
  3339.     }
  3340.     /* check actually passes through the graph area */
  3341.     if (ix > x_max && inrange(iy, y_min, y_max)) {
  3342.         lx[0] = x_min;
  3343.         ly[0] = iy;
  3344.  
  3345.         lx[1] = x_max;
  3346.         ly[1] = iy;
  3347. #if 0
  3348.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3349.             lx[0], ly[0], lx[1], ly[1]);
  3350. #endif
  3351.         return (TRUE);
  3352.     } else {
  3353. #if 0
  3354.         fprintf(stderr, "\tB\n");
  3355. #endif
  3356.         return (FALSE);
  3357.     }
  3358.     }
  3359.     if (oy == -VERYLARGE || iy == -VERYLARGE) {
  3360.     if (iy == -VERYLARGE) {
  3361.         /* swap points so ix/iy don't have a -VERYLARGE component */
  3362.         swap = ix;
  3363.         ix = ox;
  3364.         ox = swap;
  3365.         swap = iy;
  3366.         iy = oy;
  3367.         oy = swap;
  3368.     }
  3369.     /* check actually passes through the graph area */
  3370.     if (iy > y_max && inrange(ix, x_min, x_max)) {
  3371.         lx[0] = ix;
  3372.         ly[0] = y_min;
  3373.  
  3374.         lx[1] = ix;
  3375.         ly[1] = y_max;
  3376. #if 0
  3377.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3378.             lx[0], ly[0], lx[1], ly[1]);
  3379. #endif
  3380.         return (TRUE);
  3381.     } else {
  3382. #if 0
  3383.         fprintf(stderr, "\tC\n");
  3384. #endif
  3385.         return (FALSE);
  3386.     }
  3387.     }
  3388.     /*
  3389.      * Special horizontal/vertical, etc. cases are checked and remaining
  3390.      * slant lines are checked separately.
  3391.      *
  3392.      * The slant line intersections are solved using the parametric form
  3393.      * of the equation for a line, since if we test x/y min/max planes explicitly
  3394.      * then e.g. a  line passing through a corner point (x_min,y_min) 
  3395.      * actually intersects 2 planes and hence further tests would be required 
  3396.      * to anticipate this and similar situations.
  3397.      */
  3398.  
  3399.     /*
  3400.      * Can have case (ix == ox && iy == oy) as both points OUTRANGE
  3401.      */
  3402.     if (ix == ox && iy == oy) {
  3403.     /* but as only define single outrange point, can't intersect graph area */
  3404.     return (FALSE);
  3405.     }
  3406.     if (ix == ox) {
  3407.     /* line parallel to y axis */
  3408.  
  3409.     /* x coord must be in range, and line must span both y_min and y_max */
  3410.     /* note that spanning y_min implies spanning y_max, as both points OUTRANGE */
  3411.     if (!inrange(ix, x_min, x_max)) {
  3412.         return (FALSE);
  3413.     }
  3414.     if (inrange(y_min, iy, oy)) {
  3415.         lx[0] = ix;
  3416.         ly[0] = y_min;
  3417.  
  3418.         lx[1] = ix;
  3419.         ly[1] = y_max;
  3420. #if 0
  3421.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3422.             lx[0], ly[0], lx[1], ly[1]);
  3423. #endif
  3424.         return (TRUE);
  3425.     } else
  3426.         return (FALSE);
  3427.     }
  3428.     if (iy == oy) {
  3429.     /* already checked case (ix == ox && iy == oy) */
  3430.  
  3431.     /* line parallel to x axis */
  3432.     /* y coord must be in range, and line must span both x_min and x_max */
  3433.     /* note that spanning x_min implies spanning x_max, as both points OUTRANGE */
  3434.     if (!inrange(iy, y_min, y_max)) {
  3435.         return (FALSE);
  3436.     }
  3437.     if (inrange(x_min, ix, ox)) {
  3438.         lx[0] = x_min;
  3439.         ly[0] = iy;
  3440.  
  3441.         lx[1] = x_max;
  3442.         ly[1] = iy;
  3443. #if 0
  3444.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3445.             lx[0], ly[0], lx[1], ly[1]);
  3446. #endif
  3447.         return (TRUE);
  3448.     } else
  3449.         return (FALSE);
  3450.     }
  3451.     /* nasty 2D slanted line in an xy plane */
  3452.  
  3453.     /*
  3454.        Solve parametric equation
  3455.  
  3456.        (ix, iy) + t (diff_x, diff_y)
  3457.  
  3458.        where 0.0 <= t <= 1.0 and
  3459.  
  3460.        diff_x = (ox - ix);
  3461.        diff_y = (oy - iy);
  3462.      */
  3463.  
  3464.     t[0] = (x_min - ix) / (ox - ix);
  3465.     t[1] = (x_max - ix) / (ox - ix);
  3466.  
  3467.     if (t[0] > t[1]) {
  3468.     swap = t[0];
  3469.     t[0] = t[1];
  3470.     t[1] = swap;
  3471.     }
  3472.     t[2] = (y_min - iy) / (oy - iy);
  3473.     t[3] = (y_max - iy) / (oy - iy);
  3474.  
  3475.     if (t[2] > t[3]) {
  3476.     swap = t[2];
  3477.     t[2] = t[3];
  3478.     t[3] = swap;
  3479.     }
  3480.     t_min = GPMAX(GPMAX(t[0], t[2]), 0.0);
  3481.     t_max = GPMIN(GPMIN(t[1], t[3]), 1.0);
  3482.  
  3483.     if (t_min > t_max)
  3484.     return (FALSE);
  3485.  
  3486.     lx[0] = ix + t_min * (ox - ix);
  3487.     ly[0] = iy + t_min * (oy - iy);
  3488.  
  3489.     lx[1] = ix + t_max * (ox - ix);
  3490.     ly[1] = iy + t_max * (oy - iy);
  3491.  
  3492.     /*
  3493.      * Can only have 0 or 2 intersection points -- only need test one coord
  3494.      */
  3495.     if (inrange(lx[0], x_min, x_max) &&
  3496.     inrange(ly[0], y_min, y_max)) {
  3497. #if 0
  3498.     fprintf(stderr, "(%g %g) -> (%g %g)",
  3499.         lx[0], ly[0], lx[1], ly[1]);
  3500. #endif
  3501.     return (TRUE);
  3502.     }
  3503.     return (FALSE);
  3504. }
  3505.  
  3506. /* justify ticplace to a proper date-time value */
  3507. double time_tic_just(level, ticplace)
  3508. int level;
  3509. double ticplace;
  3510. {
  3511.     struct tm tm;
  3512.  
  3513.     if (level <= 0) {
  3514.     return (ticplace);
  3515.     }
  3516.     ggmtime(&tm, (double) ticplace);
  3517.     if (level > 0) {        /* units of minutes */
  3518.     if (tm.tm_sec > 50)
  3519.         tm.tm_min++;
  3520.     tm.tm_sec = 0;
  3521.     }
  3522.     if (level > 1) {        /* units of hours */
  3523.     if (tm.tm_min > 50)
  3524.         tm.tm_hour++;
  3525.     tm.tm_min = 0;
  3526.     }
  3527.     if (level > 2) {        /* units of days */
  3528.     if (tm.tm_hour > 14) {
  3529.         tm.tm_hour = 0;
  3530.         tm.tm_mday = 0;
  3531.         tm.tm_yday++;
  3532.         ggmtime(&tm, (double) gtimegm(&tm));
  3533.     } else if (tm.tm_hour > 7) {
  3534.         tm.tm_hour = 12;
  3535.     } else if (tm.tm_hour > 3) {
  3536.         tm.tm_hour = 6;
  3537.     } else {
  3538.         tm.tm_hour = 0;
  3539.     }
  3540.     }
  3541.     /* skip it, I have not bothered with weekday so far */
  3542.     if (level > 4) {        /* units of month */
  3543.     if (tm.tm_mday > 25) {
  3544.         tm.tm_mon++;
  3545.         if (tm.tm_mon > 11) {
  3546.         tm.tm_year++;
  3547.         tm.tm_mon = 0;
  3548.         }
  3549.     }
  3550.     tm.tm_mday = 1;
  3551.     }
  3552.     if (level > 5) {
  3553.     if (tm.tm_mon >= 7)
  3554.         tm.tm_year++;
  3555.     tm.tm_mon = 0;
  3556.     }
  3557.     ticplace = (double) gtimegm(&tm);
  3558.     ggmtime(&tm, (double) gtimegm(&tm));
  3559.     return (ticplace);
  3560. }
  3561.  
  3562. /* make smalltics for time-axis */
  3563. double make_ltic(tlevel, incr)
  3564. int tlevel;
  3565. double incr;
  3566. {
  3567.     double tinc;
  3568.     tinc = 0;
  3569.     if (tlevel < 0)
  3570.     tlevel = 0;
  3571.     switch (tlevel) {
  3572.     case 0:
  3573.     case 1:{
  3574.         if (incr >= 20)
  3575.         tinc = 10;
  3576.         if (incr >= 60)
  3577.         tinc = 30;
  3578.         if (incr >= 2 * 60)
  3579.         tinc = 60;
  3580.         if (incr >= 5 * 60)
  3581.         tinc = 2 * 60;
  3582.         if (incr >= 10 * 60)
  3583.         tinc = 5 * 60;
  3584.         if (incr >= 20 * 60)
  3585.         tinc = 10 * 60;
  3586.         break;
  3587.     }
  3588.     case 2:{
  3589.         if (incr >= 20 * 60)
  3590.         tinc = 10 * 60;
  3591.         if (incr >= 3600)
  3592.         tinc = 30 * 60;
  3593.         if (incr >= 2 * 3600)
  3594.         tinc = 3600;
  3595.         if (incr >= 5 * 3600)
  3596.         tinc = 2 * 3600;
  3597.         if (incr >= 10 * 3600)
  3598.         tinc = 5 * 3600;
  3599.         if (incr >= 20 * 3600)
  3600.         tinc = 10 * 3600;
  3601.         break;
  3602.     }
  3603.     case 3:{
  3604.         if (incr > 2 * 3600)
  3605.         tinc = 3600;
  3606.         if (incr > 4 * 3600)
  3607.         tinc = 2 * 3600;
  3608.         if (incr > 7 * 3600)
  3609.         tinc = 3 * 3600;
  3610.         if (incr > 13 * 3600)
  3611.         tinc = 6 * 3600;
  3612.         if (incr > DAY_SEC)
  3613.         tinc = 12 * 3600;
  3614.         if (incr > 2 * DAY_SEC)
  3615.         tinc = DAY_SEC;
  3616.         break;
  3617.     }
  3618.     case 4:{            /* week: tic per day */
  3619.         if (incr > 2 * DAY_SEC)
  3620.         tinc = DAY_SEC;
  3621.         if (incr > 7 * DAY_SEC)
  3622.         tinc = 7 * DAY_SEC;
  3623.         break;
  3624.     }
  3625.     case 5:{            /* month */
  3626.         if (incr > 2 * DAY_SEC)
  3627.         tinc = DAY_SEC;
  3628.         if (incr > 15 * DAY_SEC)
  3629.         tinc = 10 * DAY_SEC;
  3630.         if (incr > 2 * MON_SEC)
  3631.         tinc = MON_SEC;
  3632.         if (incr > 6 * MON_SEC)
  3633.         tinc = 3 * MON_SEC;
  3634.         if (incr > 2 * YEAR_SEC)
  3635.         tinc = YEAR_SEC;
  3636.         break;
  3637.     }
  3638.     case 6:{            /* year */
  3639.         if (incr > 2 * MON_SEC)
  3640.         tinc = MON_SEC;
  3641.         if (incr > 6 * MON_SEC)
  3642.         tinc = 3 * MON_SEC;
  3643.         if (incr > 2 * YEAR_SEC)
  3644.         tinc = YEAR_SEC;
  3645.         if (incr > 10 * YEAR_SEC)
  3646.         tinc = 5 * YEAR_SEC;
  3647.         if (incr > 50 * YEAR_SEC)
  3648.         tinc = 10 * YEAR_SEC;
  3649.         if (incr > 100 * YEAR_SEC)
  3650.         tinc = 20 * YEAR_SEC;
  3651.         if (incr > 200 * YEAR_SEC)
  3652.         tinc = 50 * YEAR_SEC;
  3653.         if (incr > 300 * YEAR_SEC)
  3654.         tinc = 100 * YEAR_SEC;
  3655.         break;
  3656.     }
  3657.     }
  3658.     return (tinc);
  3659. }
  3660.  
  3661.  
  3662. void write_multiline(x, y, text, hor, vert, angle, font)
  3663. unsigned int x, y;
  3664. char *text;
  3665. enum JUSTIFY hor;        /* horizontal ... */
  3666. int vert;            /* ... and vertical just - text in hor direction despite angle */
  3667. int angle;            /* assume term has already been set for this */
  3668. char *font;            /* NULL or "" means use default */
  3669. {
  3670.     /* assumes we are free to mangle the text */
  3671.     register struct termentry *t = term;
  3672.     char *p;
  3673.     if (vert != JUST_TOP) {
  3674.     /* count lines and adjust y */
  3675.     int lines = 0;        /* number of linefeeds - one fewer than lines */
  3676.     for (p = text; *p; ++p)
  3677.         if (*p == '\n')
  3678.         ++lines;
  3679.     if (angle)
  3680.         x -= (vert * lines * t->v_char) / 2;
  3681.     else
  3682.         y += (vert * lines * t->v_char) / 2;
  3683.     }
  3684.     if (font && *font)
  3685.     (*t->set_font) (font);
  3686.  
  3687.  
  3688.     for (;;) {            /* we will explicitly break out */
  3689.  
  3690.     if ((p = strchr(text, '\n')) != NULL)
  3691.         *p = 0;        /* terminate the string */
  3692.  
  3693.     if ((*t->justify_text) (hor)) {
  3694.         (*t->put_text) (x, y, text);
  3695.     } else {
  3696.         int fix = hor * (t->h_char) * strlen(text) / 2;
  3697.         if (angle)
  3698.         (*t->put_text) (x, y - fix, text);
  3699.         else
  3700.         (*t->put_text) (x - fix, y, text);
  3701.     }
  3702.     if (angle)
  3703.         x += t->v_char;
  3704.     else
  3705.         y -= t->v_char;
  3706.  
  3707.     if (!p)
  3708.         break;
  3709.  
  3710.     text = p + 1;
  3711.     }                /* unconditional branch back to the for(;;) - just a goto ! */
  3712.  
  3713.     if (font && *font)
  3714.     (*t->set_font) (default_font);
  3715. }
  3716.  
  3717. /* display a x-axis ticmark - called by gen_ticks */
  3718. /* also uses global tic_start, tic_direction, tic_text and tic_just */
  3719. static void xtick2d_callback(axis, place, text, grid)
  3720. int axis;
  3721. double place;
  3722. char *text;
  3723. struct lp_style_type grid;    /* linetype or -2 for no grid */
  3724. {
  3725.     register struct termentry *t = term;
  3726.     /* minitick if text is NULL - beware - h_tic is unsigned */
  3727.     int ticsize = tic_direction * (int) (t->v_tic) * (text ? ticscale : miniticscale);
  3728.     unsigned int x = map_x(place);
  3729.  
  3730.     if (grid.l_type > -2) {
  3731.     term_apply_lp_properties(&grid);
  3732.     if (polar_grid_angle) {
  3733.         double x = place, y = 0, s = sin(0.1), c = cos(0.1);
  3734.         int i;
  3735.         int ogx = map_x(x);
  3736.         int ogy = map_y(0);
  3737.         int tmpgx, tmpgy, gx, gy;
  3738.  
  3739.         if (place > largest_polar_circle)
  3740.         largest_polar_circle = place;
  3741.         else if (-place > largest_polar_circle)
  3742.         largest_polar_circle = -place;
  3743.         for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
  3744.         {
  3745.             /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
  3746.             double tx = x * c - y * s;
  3747.             /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
  3748.             y = y * c + x * s;
  3749.             x = tx;
  3750.         }
  3751.         tmpgx = gx = map_x(x);
  3752.         tmpgy = gy = map_y(y);
  3753.         if (clip_line(&ogx, &ogy, &tmpgx, &tmpgy)) {
  3754.             (*t->move) ((unsigned int) ogx, (unsigned int) ogy);
  3755.             (*t->vector) ((unsigned int) tmpgx, (unsigned int) tmpgy);
  3756.         }
  3757.         ogx = gx;
  3758.         ogy = gy;
  3759.         }
  3760.     } else {
  3761.         if (lkey && key_yt > ybot && x < key_xr && x > key_xl) {
  3762.         if (key_yb > ybot) {
  3763.             (*t->move) (x, ybot);
  3764.             (*t->vector) (x, key_yb);
  3765.         }
  3766.         if (key_yt < ytop) {
  3767.             (*t->move) (x, key_yt);
  3768.             (*t->vector) (x, ytop);
  3769.         }
  3770.         } else {
  3771.         (*t->move) (x, ybot);
  3772.         (*t->vector) (x, ytop);
  3773.         }
  3774.     }
  3775.     term_apply_lp_properties(&border_lp);    /* border linetype */
  3776.     }
  3777.     /* we precomputed tic posn and text posn in global vars */
  3778.  
  3779.     (*t->move) (x, tic_start);
  3780.     (*t->vector) (x, tic_start + ticsize);
  3781.  
  3782.     if (tic_mirror >= 0) {
  3783.     (*t->move) (x, tic_mirror);
  3784.     (*t->vector) (x, tic_mirror - ticsize);
  3785.     }
  3786.     if (text)
  3787.     write_multiline(x, tic_text, text, tic_hjust, tic_vjust, rotate_tics, NULL);
  3788. }
  3789.  
  3790. /* display a y-axis ticmark - called by gen_ticks */
  3791. /* also uses global tic_start, tic_direction, tic_text and tic_just */
  3792. static void ytick2d_callback(axis, place, text, grid)
  3793. int axis;
  3794. double place;
  3795. char *text;
  3796. struct lp_style_type grid;    /* linetype or -2 */
  3797. {
  3798.     register struct termentry *t = term;
  3799.     /* minitick if text is NULL - v_tic is unsigned */
  3800.     int ticsize = tic_direction * (int) (t->h_tic) * (text ? ticscale : miniticscale);
  3801.     unsigned int y = map_y(place);
  3802.     if (grid.l_type > -2) {
  3803.     term_apply_lp_properties(&grid);
  3804.     if (polar_grid_angle) {
  3805.         double x = 0, y = place, s = sin(0.1), c = cos(0.1);
  3806.         int i;
  3807.         if (place > largest_polar_circle)
  3808.         largest_polar_circle = place;
  3809.         else if (-place > largest_polar_circle)
  3810.         largest_polar_circle = -place;
  3811.         clip_move(map_x(x), map_y(y));
  3812.         for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
  3813.         {
  3814.             /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
  3815.             double tx = x * c - y * s;
  3816.             /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
  3817.             y = y * c + x * s;
  3818.             x = tx;
  3819.         }
  3820.         clip_vector(map_x(x), map_y(y));
  3821.         }
  3822.     } else {
  3823.         if (lkey && y < key_yt && y > key_yb && key_xl < xright /* catch TOUT */ ) {
  3824.         if (key_xl > xleft) {
  3825.             (*t->move) (xleft, y);
  3826.             (*t->vector) (key_xl, y);
  3827.         }
  3828.         if (key_xr < xright) {
  3829.             (*t->move) (key_xr, y);
  3830.             (*t->vector) (xright, y);
  3831.         }
  3832.         } else {
  3833.         (*t->move) (xleft, y);
  3834.         (*t->vector) (xright, y);
  3835.         }
  3836.     }
  3837.     term_apply_lp_properties(&border_lp);    /* border linetype */
  3838.     }
  3839.     /* we precomputed tic posn and text posn */
  3840.  
  3841.     (*t->move) (tic_start, y);
  3842.     (*t->vector) (tic_start + ticsize, y);
  3843.  
  3844.     if (tic_mirror >= 0) {
  3845.     (*t->move) (tic_mirror, y);
  3846.     (*t->vector) (tic_mirror - ticsize, y);
  3847.     }
  3848.     if (text)
  3849.     write_multiline(tic_text, y, text, tic_hjust, tic_vjust, rotate_tics, NULL);
  3850. }
  3851.  
  3852. int label_width(str, lines)
  3853. char *str;
  3854. int *lines;
  3855. {
  3856.     char lab[MAX_LINE_LEN + 1], *s, *e;
  3857.     int mlen, len, l;
  3858.  
  3859.     l = mlen = len = 0;
  3860.     sprintf(lab, "%s\n", str);
  3861.     s = lab;
  3862.     while ((e = (char *) strchr(s, '\n')) != NULL) {    /* HBB 980308: quiet BC-3.1 warning */
  3863.     *e = '\0';
  3864.     len = strlen(s);    /* = e-s ? */
  3865.     if (len > mlen)
  3866.         mlen = len;
  3867.     if (len || l)
  3868.         l++;
  3869.     s = ++e;
  3870.     }
  3871.     /* lines = NULL => not interested - div */
  3872.     if (lines)
  3873.     *lines = l;
  3874.     return (mlen);
  3875. }
  3876.  
  3877.  
  3878. void setup_tics(axis, ticdef, format, max)
  3879. int axis;
  3880. struct ticdef *ticdef;
  3881. char *format;
  3882. int max;            /* approx max number of slots available */
  3883. {
  3884.     double tic = 0;        /* HBB: shut up gcc -Wall */
  3885.  
  3886.     int fixmin = (auto_array[axis] & 1) != 0;
  3887.     int fixmax = (auto_array[axis] & 2) != 0;
  3888.  
  3889.     if (ticdef->type == TIC_SERIES) {
  3890.     ticstep[axis] = tic = ticdef->def.series.incr;
  3891.     fixmin &= (ticdef->def.series.start == -VERYLARGE);
  3892.     fixmax &= (ticdef->def.series.end == VERYLARGE);
  3893.     } else if (ticdef->type == TIC_COMPUTED) {
  3894.     ticstep[axis] = tic = make_tics(axis, max);
  3895.     } else {
  3896.     fixmin = fixmax = 0;    /* user-defined, day or month */
  3897.     }
  3898.  
  3899.     if (fixmin) {
  3900.     if (min_array[axis] < max_array[axis])
  3901.         min_array[axis] = tic * floor(min_array[axis] / tic);
  3902.     else
  3903.         min_array[axis] = tic * ceil(min_array[axis] / tic);
  3904.     }
  3905.     if (fixmax) {
  3906.     if (min_array[axis] < max_array[axis])
  3907.         max_array[axis] = tic * ceil(max_array[axis] / tic);
  3908.     else
  3909.         max_array[axis] = tic * floor(max_array[axis] / tic);
  3910.     }
  3911.     if (datatype[axis] == TIME && format_is_numeric[axis])
  3912.     /* invent one for them */
  3913.     timetic_format(axis, min_array[axis], max_array[axis]);
  3914.     else
  3915.     strcpy(ticfmt[axis], format);
  3916. }
  3917.  
  3918. /*{{{  mant_exp - split into mantissa and/or exponent */
  3919. static void mant_exp(log_base, x, scientific, m, p)
  3920. double log_base, x;
  3921. int scientific;            /* round to power of 3 */
  3922. double *m;
  3923. int *p;                /* results */
  3924. {
  3925.     int sign = 1;
  3926.     double l10;
  3927.     int power;
  3928.     /*{{{  check 0 */
  3929.     if (x == 0) {
  3930.     if (m)
  3931.         *m = 0;
  3932.     if (p)
  3933.         *p = 0;
  3934.     return;
  3935.     }
  3936.     /*}}} */
  3937.     /*{{{  check -ve */
  3938.     if (x < 0) {
  3939.     sign = (-1);
  3940.     x = (-x);
  3941.     }
  3942.     /*}}} */
  3943.  
  3944.     l10 = log10(x) / log_base;
  3945.     power = floor(l10);
  3946.     if (scientific) {
  3947.     power = 3 * floor(power / 3.0);
  3948.     }
  3949.     if (m)
  3950.     *m = sign * pow(10.0, (l10 - power) * log_base);
  3951.     if (p)
  3952.     *p = power;
  3953. }
  3954. /*}}} */
  3955.  
  3956. /*
  3957.  * Kludge alert!!
  3958.  * Workaround until we have a better solution ...
  3959.  * Note: this assumes that all calls to sprintf in gprintf have
  3960.  * exactly three args. Lars
  3961.  */
  3962. #ifdef HAVE_SNPRINTF
  3963. # define sprintf(str,fmt,arg) \
  3964.     if (snprintf((str),count,(fmt),(arg)) > count) \
  3965.       fprintf (stderr,"%s:%d: Warning: too many digits for format\n",__FILE__,__LINE__)
  3966. #endif
  3967.  
  3968. /*{{{  gprintf */
  3969. /* extended snprintf */
  3970. static void gprintf(dest, count, format, log_base, x)
  3971. char *dest, *format;
  3972. size_t count;
  3973. double log_base, x;        /* we print one number in a number of different formats */
  3974. {
  3975. #define LOC_PI 3.14159265358979323846    /* local definition of PI */
  3976.     char temp[MAX_LINE_LEN];
  3977.     char *t;
  3978.  
  3979.     for (;;) {
  3980.     /*{{{  copy to dest until % */
  3981.     while (*format != '%')
  3982.         if (!(*dest++ = *format++))
  3983.         return;        /* end of format */
  3984.     /*}}} */
  3985.  
  3986.     /*{{{  check for %% */
  3987.     if (format[1] == '%') {
  3988.         *dest++ = '%';
  3989.         format += 2;
  3990.         continue;
  3991.     }
  3992.     /*}}} */
  3993.  
  3994.     /*{{{  copy format part to temp, excluding conversion character */
  3995.     t = temp;
  3996.     *t++ = '%';
  3997.     /* dont put isdigit first since sideeffect in macro is bad */
  3998.     while (*++format == '.' || isdigit((int) *format) ||
  3999.            *format == '-' || *format == '+' || *format == ' ')
  4000.         *t++ = *format;
  4001.     /*}}} */
  4002.  
  4003.     /*{{{  convert conversion character */
  4004.     switch (*format) {
  4005.         /*{{{  x and o */
  4006.     case 'x':
  4007.     case 'X':
  4008.     case 'o':
  4009.     case 'O':
  4010.         t[0] = *format++;
  4011.         t[1] = 0;
  4012.         sprintf(dest, temp, (int) x);
  4013.         dest += strlen(dest);
  4014.         break;
  4015.         /*}}} */
  4016.         /*{{{  e, f and g */
  4017.     case 'e':
  4018.     case 'E':
  4019.     case 'f':
  4020.     case 'F':
  4021.     case 'g':
  4022.     case 'G':
  4023.         t[0] = *format++;
  4024.         t[1] = 0;
  4025.         sprintf(dest, temp, x);
  4026.         dest += strlen(dest);
  4027.         break;
  4028.         /*}}} */
  4029.         /*{{{  l */
  4030.     case 'l':
  4031.         {
  4032.         double mantissa;
  4033.         mant_exp(log_base, x, 0, &mantissa, NULL);
  4034.         t[0] = 'f';
  4035.         t[1] = 0;
  4036.         sprintf(dest, temp, mantissa);
  4037.         dest += strlen(dest);
  4038.         ++format;
  4039.         break;
  4040.         }
  4041.         /*}}} */
  4042.         /*{{{  t */
  4043.     case 't':
  4044.         {
  4045.         double mantissa;
  4046.         mant_exp(1.0, x, 0, &mantissa, NULL);
  4047.         t[0] = 'f';
  4048.         t[1] = 0;
  4049.         sprintf(dest, temp, mantissa);
  4050.         dest += strlen(dest);
  4051.         ++format;
  4052.         break;
  4053.         }
  4054.         /*}}} */
  4055.         /*{{{  s */
  4056.     case 's':
  4057.         {
  4058.         double mantissa;
  4059.         mant_exp(1.0, x, 1, &mantissa, NULL);
  4060.         t[0] = 'f';
  4061.         t[1] = 0;
  4062.         sprintf(dest, temp, mantissa);
  4063.         dest += strlen(dest);
  4064.         ++format;
  4065.         break;
  4066.         }
  4067.         /*}}} */
  4068.         /*{{{  L */
  4069.     case 'L':
  4070.         {
  4071.         int power;
  4072.         mant_exp(log_base, x, 0, NULL, &power);
  4073.         t[0] = 'd';
  4074.         t[1] = 0;
  4075.         sprintf(dest, temp, power);
  4076.         dest += strlen(dest);
  4077.         ++format;
  4078.         break;
  4079.         }
  4080.         /*}}} */
  4081.         /*{{{  T */
  4082.     case 'T':
  4083.         {
  4084.         int power;
  4085.         mant_exp(1.0, x, 0, NULL, &power);
  4086.         t[0] = 'd';
  4087.         t[1] = 0;
  4088.         sprintf(dest, temp, power);
  4089.         dest += strlen(dest);
  4090.         ++format;
  4091.         break;
  4092.         }
  4093.         /*}}} */
  4094.         /*{{{  S */
  4095.     case 'S':
  4096.         {
  4097.         int power;
  4098.         mant_exp(1.0, x, 1, NULL, &power);
  4099.         t[0] = 'd';
  4100.         t[1] = 0;
  4101.         sprintf(dest, temp, power);
  4102.         dest += strlen(dest);
  4103.         ++format;
  4104.         break;
  4105.         }
  4106.         /*}}} */
  4107.         /*{{{  c */
  4108.     case 'c':
  4109.         {
  4110.         int power;
  4111.         mant_exp(1.0, x, 1, NULL, &power);
  4112.         t[0] = 'c';
  4113.         t[1] = 0;
  4114.         power = power / 3 + 6;    /* -18 -> 0, 0 -> 6, +18 -> 12, ... */
  4115.         if (power >= 0 && power <= 12) {
  4116.             sprintf(dest, temp, "afpnum kMGTPE"[power]);
  4117.         } else {
  4118.             /* please extend the range ! */
  4119.             /* name  power   name  power
  4120.                -------------------------
  4121.                atto   -18    Exa    18
  4122.                femto  -15    Peta   15
  4123.                pico   -12    Tera   12
  4124.                nano    -9    Giga    9
  4125.                micro   -6    Mega    6
  4126.                milli   -3    kilo    3   */
  4127.  
  4128.             /* for the moment, print e+21 for example */
  4129.             sprintf(dest, "e%+02d", (power - 6) * 3);
  4130.         }
  4131.  
  4132.         dest += strlen(dest);
  4133.         ++format;
  4134.         break;
  4135.         }
  4136.     case 'P':
  4137.         {
  4138.         t[0] = 'f';
  4139.         t[1] = 0;
  4140.         sprintf(dest, temp, x / LOC_PI);
  4141.         dest += strlen(dest);
  4142.         ++format;
  4143.         break;
  4144.         }
  4145.         /*}}} */
  4146.     default:
  4147.         int_error("Bad format character", NO_CARET);
  4148.     }
  4149.     /*}}} */
  4150.     }
  4151. }
  4152. #undef LOC_PI            /* local definition of PI */
  4153. /*}}} */
  4154. #ifdef HAVE_SNPRINTF
  4155. # undef sprintf
  4156. #endif
  4157.  
  4158.  
  4159.  
  4160. /*{{{  gen_tics */
  4161. /* uses global arrays ticstep[], ticfmt[], min_array[], max_array[],
  4162.  * auto_array[], log_array[], log_base_array[]
  4163.  * we use any of GRID_X/Y/X2/Y2 and  _MX/_MX2/etc - caller is expected
  4164.  * to clear the irrelevent fields from global grid bitmask
  4165.  * note this is also called from graph3d, so we need GRID_Z too
  4166.  */
  4167.  
  4168. void gen_tics(axis, def, grid, minitics, minifreq, callback)
  4169. int axis;            /* FIRST_X_AXIS, etc */
  4170. struct ticdef *def;        /* tic defn */
  4171. int grid;            /* GRID_X | GRID_MX etc */
  4172. int minitics;            /* minitics - off/default/auto/explicit */
  4173. double minifreq;        /* frequency */
  4174. tic_callback callback;        /* fn to call to actually do the work */
  4175. {
  4176.     /* seperate main-tic part of grid */
  4177.     struct lp_style_type lgrd, mgrd;
  4178.  
  4179.     memcpy(&lgrd, &grid_lp, sizeof(struct lp_style_type));
  4180.     memcpy(&mgrd, &mgrid_lp, sizeof(struct lp_style_type));
  4181.     lgrd.l_type = (grid & (GRID_X | GRID_Y | GRID_X2 | GRID_Y2 | GRID_Z)) ? grid_lp.l_type : -2;
  4182.     mgrd.l_type = (grid & (GRID_MX | GRID_MY | GRID_MX2 | GRID_MY2 | GRID_MZ)) ? mgrid_lp.l_type : -2;
  4183.  
  4184.     if (def->type == TIC_USER) {    /* special case */
  4185.     /*{{{  do user tics then return */
  4186.     struct ticmark *mark = def->def.user;
  4187.     double uncertain = (max_array[axis] - min_array[axis]) / 10;
  4188.     double ticmin = min_array[axis] - SIGNIF * uncertain;
  4189.     double internal_max = max_array[axis] + SIGNIF * uncertain;
  4190.     double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
  4191.  
  4192.     /* polar labels always +ve, and if rmin has been set, they are
  4193.      * relative to rmin. position is as user specified, but must
  4194.      * be translated. I dont think it will work at all for
  4195.      * log scale, so I shan't worry about it !
  4196.      */
  4197.     double polar_shift = (polar && !(autoscale_r & 1)) ? rmin : 0;
  4198.  
  4199.     for (mark = def->def.user; mark; mark = mark->next) {
  4200.         char label[64];
  4201.         double internal = log_array[axis] ? log(mark->position) / log_base_array[axis] : mark->position;
  4202.  
  4203.         internal -= polar_shift;
  4204.  
  4205.         if (!inrange(internal, ticmin, internal_max))
  4206.         continue;
  4207.  
  4208.         if (datatype[axis] == TIME)
  4209.         gstrftime(label, 24, mark->label ? mark->label : ticfmt[axis], mark->position);
  4210.         else
  4211.         gprintf(label, sizeof(label), mark->label ? mark->label : ticfmt[axis], log_base, mark->position);
  4212.  
  4213.         (*callback) (axis, internal, label, lgrd);
  4214.     }
  4215.  
  4216.     return;            /* NO MINITICS FOR USER-DEF TICS */
  4217.     /*}}} */
  4218.     }
  4219.     /* series-tics
  4220.      * need to distinguish user co-ords from internal co-ords.
  4221.      * - for logscale, internal = log(user), else internal = user
  4222.      *
  4223.      * The minitics are a bit of a drag - we need to distinuish
  4224.      * the cases step>1 from step == 1.
  4225.      * If step = 1, we are looking at 1,10,100,1000 for example, so
  4226.      * minitics are 2,5,8, ...  - done in user co-ordinates
  4227.      * If step>1, we are looking at 1,1e6,1e12 for example, so
  4228.      * minitics are 10,100,1000,... - done in internal co-ords
  4229.      */
  4230.  
  4231.     {
  4232.     double tic;        /* loop counter */
  4233.     double internal;    /* in internal co-ords */
  4234.     double user;        /* in user co-ords */
  4235.     double start, step, end;
  4236.     double lmin = min_array[axis], lmax = max_array[axis];
  4237.     double internal_min, internal_max;    /* to allow for rounding errors */
  4238.     double ministart = 0, ministep = 1, miniend = 1;    /* internal or user - depends on step */
  4239.     int anyticput = 0;    /* for detection of infinite loop */
  4240.  
  4241.     /* gprintf uses log10() of base - log_base_array is log() */
  4242.     double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
  4243.  
  4244.     if (lmax < lmin) {
  4245.         /* hmm - they have set reversed range for some reason */
  4246.         double temp = lmin;
  4247.         lmin = lmax;
  4248.         lmax = temp;
  4249.     }
  4250.     /*{{{  choose start, step and end */
  4251.     switch (def->type) {
  4252.     case TIC_SERIES:
  4253.         if (log_array[axis]) {
  4254.         /* we can tolerate start <= 0 if step and end > 0 */
  4255.         if (def->def.series.end <= 0 ||
  4256.             def->def.series.incr <= 0)
  4257.             return;    /* just quietly ignore */
  4258.         step = log(def->def.series.incr) / log_base_array[axis];
  4259.         end = log(def->def.series.end) / log_base_array[axis];
  4260.         start = def->def.series.start > 0 ?
  4261.             log(def->def.series.start) / log_base_array[axis] :
  4262.             step;
  4263.         } else {
  4264.         start = def->def.series.start;
  4265.         step = def->def.series.incr;
  4266.         end = def->def.series.end;
  4267.         if (start == -VERYLARGE)
  4268.             start = step * floor(lmin / step);
  4269.         if (end == VERYLARGE)
  4270.             end = step * ceil(lmax / step);
  4271.         }
  4272.         break;
  4273.     case TIC_COMPUTED:
  4274.         /* round to multiple of step */
  4275.         start = ticstep[axis] * floor(lmin / ticstep[axis]);
  4276.         step = ticstep[axis];
  4277.         end = ticstep[axis] * ceil(lmax / ticstep[axis]);
  4278.         break;
  4279.     case TIC_MONTH:
  4280.         start = floor(lmin);
  4281.         end = ceil(lmax);
  4282.         step = floor((end - start) / 12);
  4283.         if (step < 1)
  4284.         step = 1;
  4285.         break;
  4286.     case TIC_DAY:
  4287.         start = floor(lmin);
  4288.         end = ceil(lmax);
  4289.         step = floor((end - start) / 14);
  4290.         if (step < 1)
  4291.         step = 1;
  4292.         break;
  4293.     default:
  4294.         graph_error("Internal error : unknown tic type");
  4295.         return;        /* avoid gcc -Wall warning about start */
  4296.     }
  4297.     /*}}} */
  4298.  
  4299.     /*{{{  ensure ascending order */
  4300.     if (end < start) {
  4301.         double temp;
  4302.         temp = end;
  4303.         end = start;
  4304.         start = temp;
  4305.     }
  4306.     step = fabs(step);
  4307.     /*}}} */
  4308.  
  4309.     if (minitics) {
  4310.         /*{{{  figure out ministart, ministep, miniend */
  4311.         if (minitics == MINI_USER) {
  4312.         /* they have said what they want */
  4313.         if (minifreq <= 0)
  4314.             minitics = 0;    /* not much else we can do */
  4315.         else if (log_array[axis]) {
  4316.             ministart = ministep = step / minifreq * base_array[axis];
  4317.             miniend = step * base_array[axis];
  4318.         } else {
  4319.             ministart = ministep = step / minifreq;
  4320.             miniend = step;
  4321.         }
  4322.         } else if (log_array[axis]) {
  4323.         if (step > 1.5) {    /* beware rounding errors */
  4324.             /*{{{  10,100,1000 case */
  4325.             /* no more than five minitics */
  4326.             ministart = ministep = (int) (0.2 * step);
  4327.             if (ministep < 1)
  4328.             ministart = ministep = 1;
  4329.             miniend = step;
  4330.             /*}}} */
  4331.         } else {
  4332.             /*{{{  2,5,8 case */
  4333.             miniend = base_array[axis];
  4334.             if (end - start >= 10)
  4335.             minitics = 0;    /* none */
  4336.             else if (end - start >= 5) {
  4337.             ministart = 2;
  4338.             ministep = 3;
  4339.             } else {
  4340.             ministart = 2;
  4341.             ministep = 1;
  4342.             }
  4343.             /*}}} */
  4344.         }
  4345.         } else if (datatype[axis] == TIME) {
  4346.         ministart = ministep = make_ltic(timelevel[axis], step);
  4347.         miniend = step * 0.9;
  4348.         } else if (minitics == MINI_AUTO) {
  4349.         ministart = ministep = 0.1 * step;
  4350.         miniend = step;
  4351.         } else
  4352.         minitics = 0;
  4353.  
  4354.         if (ministep <= 0)
  4355.         minitics = 0;    /* dont get stuck in infinite loop */
  4356.         /*}}} */
  4357.     }
  4358.     /*{{{  a few tweaks and checks */
  4359.     /* watch rounding errors */
  4360.     end += SIGNIF * step;
  4361.     internal_max = lmax + step * SIGNIF;
  4362.     internal_min = lmin - step * SIGNIF;
  4363.  
  4364.     if (step == 0)
  4365.         return;        /* just quietly ignore them ! */
  4366.     /*}}} */
  4367.  
  4368.     for (tic = start; tic <= end; tic += step) {
  4369.         if (anyticput == 2)    /* See below... */
  4370.         break;
  4371.         if (anyticput && (fabs(tic - start) < DBL_EPSILON)) {
  4372.         /* step is too small.. */
  4373.         anyticput = 2;    /* Don't try again. */
  4374.         tic = end;    /* Put end tic. */
  4375.         } else
  4376.         anyticput = 1;
  4377.  
  4378.         /*{{{  calc internal and user co-ords */
  4379.         if (!log_array[axis]) {
  4380.         internal = datatype[axis] == TIME ? time_tic_just(timelevel[axis], tic) : tic;
  4381.         user = CheckZero(internal, step);
  4382.         } else {
  4383.         /* log scale => dont need to worry about zero ? */
  4384.         internal = tic;
  4385.         user = pow(base_array[axis], internal);
  4386.         }
  4387.         /*}}} */
  4388.         if (internal > internal_max)
  4389.         break;        /* gone too far - end of series = VERYLARGE perhaps */
  4390.         if (internal >= internal_min) {
  4391. /* continue; *//* maybe minitics!!!. user series starts below min ? */
  4392.  
  4393.         /*{{{  draw tick via callback */
  4394.         switch (def->type) {
  4395.         case TIC_DAY:{
  4396.             int d = (long) floor(user + 0.5) % 7;
  4397.             if (d < 0)
  4398.                 d += 7;
  4399.             (*callback) (axis, internal, abbrev_day_names[d], lgrd);
  4400.             break;
  4401.             }
  4402.         case TIC_MONTH:{
  4403.             int m = (long) floor(user - 1) % 12;
  4404.             if (m < 0)
  4405.                 m += 12;
  4406.             (*callback) (axis, internal, abbrev_month_names[m], lgrd);
  4407.             break;
  4408.             }
  4409.         default:{    /* comp or series */
  4410.             char label[64];
  4411.             if (datatype[axis] == TIME) {
  4412.                 /* If they are doing polar time plot, good luck to them */
  4413.                 gstrftime(label, 24, ticfmt[axis], (double) user);
  4414.             } else if (polar) {
  4415.                 /* if rmin is set, we stored internally with r-rmin */
  4416. #if 0                /* Igor's polar-grid patch */
  4417.                 double r = fabs(user) + (autoscale_r & 1 ? 0 : rmin);
  4418. #else
  4419.                 /* Igor removed fabs to allow -ve labels */
  4420.                 double r = user + (autoscale_r & 1 ? 0 : rmin);
  4421. #endif
  4422.                 gprintf(label, sizeof(label), ticfmt[axis], log_base, r);
  4423.             } else {
  4424.                 gprintf(label, sizeof(label), ticfmt[axis], log_base, user);
  4425.             }
  4426.             (*callback) (axis, internal, label, lgrd);
  4427.             }
  4428.         }
  4429.         /*}}} */
  4430.  
  4431.         }
  4432.         if (minitics) {
  4433.         /*{{{  process minitics */
  4434.         double mplace, mtic;
  4435.         for (mplace = ministart; mplace < miniend; mplace += ministep) {
  4436.             if (datatype[axis] == TIME)
  4437.             mtic = time_tic_just(timelevel[axis] - 1, internal + mplace);
  4438.             else
  4439.             mtic = internal + (log_array[axis] && step <= 1.5 ? log(mplace) / log_base_array[axis] : mplace);
  4440.             if (inrange(mtic, internal_min, internal_max) &&
  4441.             inrange(mtic, start - step * SIGNIF, end + step * SIGNIF))
  4442.             (*callback) (axis, mtic, NULL, mgrd);
  4443.         }
  4444.         /*}}} */
  4445.         }
  4446.     }
  4447.     }
  4448. }
  4449. /*}}} */
  4450.  
  4451. /*{{{  map_position */
  4452. static void map_position(pos, x, y, what)
  4453. struct position *pos;
  4454. unsigned int *x, *y;
  4455. char *what;
  4456. {
  4457.     switch (pos->scalex) {
  4458.     case first_axes:
  4459.     {
  4460.         double xx = LogScale(pos->x, log_array[FIRST_X_AXIS], log_base_array[FIRST_X_AXIS], what, "x");
  4461.         *x = xleft + (xx - min_array[FIRST_X_AXIS]) * scale[FIRST_X_AXIS] + 0.5;
  4462.         break;
  4463.     }
  4464.     case second_axes:
  4465.     {
  4466.         double xx = LogScale(pos->x, log_array[SECOND_X_AXIS], log_base_array[SECOND_X_AXIS], what, "x");
  4467.         *x = xleft + (xx - min_array[SECOND_X_AXIS]) * scale[SECOND_X_AXIS] + 0.5;
  4468.         break;
  4469.     }
  4470.     case graph:
  4471.     {
  4472.         *x = xleft + pos->x * (xright - xleft) + 0.5;
  4473.         break;
  4474.     }
  4475.     case screen:
  4476.     {
  4477.         register struct termentry *t = term;
  4478.         *x = pos->x * (t->xmax) + 0.5;
  4479.         break;
  4480.     }
  4481.     }
  4482.     switch (pos->scaley) {
  4483.     case first_axes:
  4484.     {
  4485.         double yy = LogScale(pos->y, log_array[FIRST_Y_AXIS], log_base_array[FIRST_Y_AXIS], what, "y");
  4486.         *y = ybot + (yy - min_array[FIRST_Y_AXIS]) * scale[FIRST_Y_AXIS] + 0.5;
  4487.         return;
  4488.     }
  4489.     case second_axes:
  4490.     {
  4491.         double yy = LogScale(pos->y, log_array[SECOND_Y_AXIS], log_base_array[SECOND_Y_AXIS], what, "y");
  4492.         *y = ybot + (yy - min_array[SECOND_Y_AXIS]) * scale[SECOND_Y_AXIS] + 0.5;
  4493.         return;
  4494.     }
  4495.     case graph:
  4496.     {
  4497.         *y = ybot + pos->y * (ytop - ybot) + 0.5;
  4498.         return;
  4499.     }
  4500.     case screen:
  4501.     {
  4502.         register struct termentry *t = term;
  4503.         *y = pos->y * (t->ymax) + 0.5;
  4504.         return;
  4505.     }
  4506.     }
  4507. }
  4508. /*}}} */
  4509.